diff --git a/qiskit_aer/backends/aer_compiler.py b/qiskit_aer/backends/aer_compiler.py index f48d6771d5..9f5e714b16 100644 --- a/qiskit_aer/backends/aer_compiler.py +++ b/qiskit_aer/backends/aer_compiler.py @@ -14,9 +14,12 @@ """ import itertools +from copy import copy +from typing import List -from qiskit.circuit import QuantumCircuit, Clbit +from qiskit.circuit import QuantumCircuit, Clbit, ParameterExpression from qiskit.extensions import Initialize +from qiskit.providers.options import Options from qiskit.pulse import Schedule, ScheduleBlock from qiskit.circuit.controlflow import ( WhileLoopOp, @@ -25,6 +28,10 @@ BreakLoopOp, ContinueLoopOp) from qiskit.compiler import transpile +from qiskit.qobj import QobjExperimentHeader +from qiskit_aer.aererror import AerError +# pylint: disable=import-error, no-name-in-module +from qiskit_aer.backends.controller_wrappers import AerCircuit, AerConfig from .backend_utils import circuit_optypes from ..library.control_flow_instructions import AerMark, AerJump @@ -325,3 +332,221 @@ def compile_circuit(circuits, basis_gates=None, optypes=None): compile a circuit that have control-flow instructions """ return AerCompiler().compile(circuits, basis_gates, optypes) + + +def generate_aer_config( + circuits: List[QuantumCircuit], + backend_options: Options, + **run_options +) -> AerConfig: + """generates a configuration to run simulation. + + Args: + circuits: circuit(s) to be converted + backend_options: backend options + run_options: run options + + Returns: + AerConfig to run Aer + """ + num_qubits = max(circuit.num_qubits for circuit in circuits) + memory_slots = max(circuit.num_clbits for circuit in circuits) + + config = AerConfig() + config.memory_slots = memory_slots + config.n_qubits = num_qubits + for key, value in backend_options.__dict__.items(): + if hasattr(config, key) and value is not None: + setattr(config, key, value) + for key, value in run_options.items(): + if hasattr(config, key) and value is not None: + setattr(config, key, value) + return config + + +def assemble_circuit(circuit: QuantumCircuit): + """assemble circuit object mapped to AER::Circuit""" + + num_qubits = circuit.num_qubits + num_memory = circuit.num_clbits + max_conditional_idx = 0 + + qreg_sizes = [] + creg_sizes = [] + global_phase = float(circuit.global_phase) + + for qreg in circuit.qregs: + qreg_sizes.append([qreg.name, qreg.size]) + for creg in circuit.cregs: + creg_sizes.append([creg.name, creg.size]) + + is_conditional = any( + getattr(inst.operation, "condition", None) for inst in circuit.data + ) + + header = QobjExperimentHeader( + n_qubits=num_qubits, + qreg_sizes=qreg_sizes, + memory_slots=num_memory, + creg_sizes=creg_sizes, + name=circuit.name, + global_phase=global_phase, + ) + + if circuit.metadata is not None: + header.metadata = circuit.metadata + + qubit_indices = {qubit: idx for idx, qubit in enumerate(circuit.qubits)} + clbit_indices = {clbit: idx for idx, clbit in enumerate(circuit.clbits)} + + aer_circ = AerCircuit() + aer_circ.set_header(header) + aer_circ.num_qubits = num_qubits + aer_circ.num_memory = num_memory + aer_circ.global_phase_angle = global_phase + + for inst in circuit.data: + # To convert to a qobj-style conditional, insert a bfunc prior + # to the conditional instruction to map the creg ?= val condition + # onto a gating register bit. + conditional_reg = -1 + if hasattr(inst.operation, "condition") and inst.operation.condition: + ctrl_reg, ctrl_val = inst.operation.condition + mask = 0 + val = 0 + if isinstance(ctrl_reg, Clbit): + mask = 1 << clbit_indices[ctrl_reg] + val = (ctrl_val & 1) << clbit_indices[ctrl_reg] + else: + for clbit, idx in clbit_indices.items(): + if clbit in ctrl_reg: + mask |= 1 << idx + val |= ((ctrl_val >> list(ctrl_reg).index(clbit)) & 1) << idx + conditional_reg = num_memory + max_conditional_idx + aer_circ.bfunc(f"0x{mask:X}", f"0x{val:X}", "==", conditional_reg) + max_conditional_idx += 1 + + _assemble_op(aer_circ, inst, qubit_indices, clbit_indices, + is_conditional, conditional_reg) + + return aer_circ + + +def _assemble_op(aer_circ, inst, qubit_indices, clbit_indices, is_conditional, conditional_reg): + operation = inst.operation + qubits = [qubit_indices[qubit] for qubit in inst.qubits] + clbits = [clbit_indices[clbit] for clbit in inst.clbits] + name = operation.name + label = operation.label + params = operation.params if hasattr(operation, "params") else None + copied = False + + for i, param in enumerate(params): + if (isinstance(param, ParameterExpression) and len(param.parameters) > 0): + if not copied: + params = copy(params) + copied = True + params[i] = 0.0 + + if name in {'ccx', 'ccz', 'cp', 'cswap', 'csx', 'cx', 'cy', 'cz', 'delay', 'ecr', + 'h', 'id', 'mcp', 'mcphase', 'mcr', 'mcrx', 'mcry', 'mcrz', 'mcswap', + 'mcsx', 'mcu', 'mcu1', 'mcu2', 'mcu3', 'mcx', 'mcx_gray', 'mcy', 'mcz', + 'p', 'r', 'rx', 'rxx', 'ry', 'ryy', 'rz', 'rzx', 'rzz', 's', 'sdg', 'swap', + 'sx', 'sxdg', 't', 'tdg', 'u', 'x', 'y', 'z', 'u1', 'u2', 'u3', + 'cu', 'cu1', 'cu2', 'cu3'}: + aer_circ.gate(name, qubits, params, [], conditional_reg, label if label else name) + elif name == 'measure': + if is_conditional: + aer_circ.measure(qubits, clbits, clbits) + else: + aer_circ.measure(qubits, clbits, []) + elif name == 'reset': + aer_circ.reset(qubits) + elif name == 'diagonal': + aer_circ.diagonal(qubits, params, label if label else 'diagonal') + elif name == 'unitary': + aer_circ.unitary(qubits, params[0], conditional_reg, label if label else 'unitary') + elif name == 'pauli': + aer_circ.gate(name, qubits, [], params, conditional_reg, label if label else name) + elif name == 'initialize': + aer_circ.initialize(qubits, params) + elif name == 'roerror': + aer_circ.roerror(qubits, params) + elif name == 'multiplexer': + aer_circ.multiplexer(qubits, params, conditional_reg, label if label else name) + elif name == 'kraus': + aer_circ.kraus(qubits, params, conditional_reg) + elif name in {'save_statevector', 'save_statevector_dict', 'save_clifford', + 'save_probabilities', 'save_probabilities_dict', 'save_matrix_product_state', + 'save_unitary', 'save_superop', 'save_density_matrix', 'save_state', + 'save_stabilizer'}: + aer_circ.save_state(qubits, name, operation._subtype, label if label else name) + elif name in {'save_amplitudes', 'save_amplitudes_sq'}: + aer_circ.save_amplitudes(qubits, name, params, operation._subtype, + label if label else name) + elif name in ('save_expval', 'save_expval_var'): + paulis = [] + coeff_reals = [] + coeff_imags = [] + for pauli, coeff in operation.params: + paulis.append(pauli) + coeff_reals.append(coeff[0]) + coeff_imags.append(coeff[1]) + aer_circ.save_expval(qubits, name, paulis, coeff_reals, coeff_imags, operation._subtype, + label if label else name) + elif name == 'set_statevector': + aer_circ.set_statevector(qubits, params) + elif name == 'set_unitary': + aer_circ.set_unitary(qubits, params) + elif name == 'set_density_matrix': + aer_circ.set_density_matrix(qubits, params) + elif name == 'set_stabilizer': + aer_circ.set_clifford(qubits, params) + elif name == 'set_superop': + aer_circ.set_superop(qubits, params) + elif name == 'set_matrix_product_state': + aer_circ.set_matrix_product_state(qubits, params) + elif name == 'superop': + aer_circ.superop(qubits, params[0], conditional_reg) + elif name == 'barrier': + pass + elif name == 'jump': + aer_circ.jump(qubits, params, conditional_reg) + elif name == 'mark': + aer_circ.mark(qubits, params) + elif name == 'qerror_loc': + aer_circ.set_qerror_loc(qubits, label if label else name, conditional_reg) + elif name in ('for_loop', 'while_loop', 'if_else'): + raise AerError('control-flow instructions must be converted ' + f'to jump and mark instructions: {name}') + + else: + raise AerError(f'unknown instruction: {name}') + + +def assemble_circuits( + circuits: List[QuantumCircuit] +) -> List[AerCircuit]: + """converts a list of Qiskit circuits into circuits mapped AER::Circuit + + Args: + circuits: circuit(s) to be converted + + Returns: + circuits to be run on the Aer backends + + Examples: + + .. code-block:: python + + from qiskit.circuit import QuantumCircuit + from qiskit_aer.backends.aer_compiler import assemble_circuits + # Create a circuit to be simulated + qc = QuantumCircuit(2, 2) + qc.h(0) + qc.cx(0, 1) + qc.measure_all() + # Generate AerCircuit from the input circuit + aer_qc_list = assemble_circuits(circuits=[qc]) + """ + return [assemble_circuit(circuit) for circuit in circuits] diff --git a/qiskit_aer/backends/aer_simulator.py b/qiskit_aer/backends/aer_simulator.py index 08097f3dc6..6a4911dbd7 100644 --- a/qiskit_aer/backends/aer_simulator.py +++ b/qiskit_aer/backends/aer_simulator.py @@ -22,8 +22,8 @@ from ..version import __version__ from .aerbackend import AerBackend, AerError -from .backend_utils import (cpp_execute, available_methods, - available_devices, +from .backend_utils import (cpp_execute_circuits, cpp_execute_qobj, + available_methods, available_devices, MAX_QUBITS_STATEVECTOR, BASIS_GATES) # pylint: disable=import-error, no-name-in-module @@ -642,7 +642,7 @@ def __repr__(self): if noise_model is None or noise_model.is_ideal(): return display pad = ' ' * (len(self.__class__.__name__) + 1) - return '{}\n{}noise_model={})'.format(display[:-1], pad, repr(noise_model)) + return f'{display[:-1]}\n{pad}noise_model={repr(noise_model)})' def name(self): """Format backend name string for simulator""" @@ -682,7 +682,7 @@ def from_backend(cls, backend, **options): # Customize configuration name name = configuration.backend_name - configuration.backend_name = 'aer_simulator({})'.format(name) + configuration.backend_name = f'aer_simulator({name})' else: raise TypeError( "The backend argument requires a BackendV2 or BackendV1 object, " @@ -730,7 +730,13 @@ def configuration(self): config.backend_name = self.name() return config - def _execute(self, qobj): + def _execute_circuits(self, aer_circuits, noise_model, config): + """Execute circuits on the backend. + """ + ret = cpp_execute_circuits(self._controller, aer_circuits, noise_model, config) + return ret + + def _execute_qobj(self, qobj): """Execute a qobj on the backend. Args: @@ -739,7 +745,7 @@ def _execute(self, qobj): Returns: dict: return a dictionary of results. """ - return cpp_execute(self._controller, qobj) + return cpp_execute_qobj(self._controller, qobj) def set_option(self, key, value): if key == "custom_instructions": @@ -748,8 +754,8 @@ def set_option(self, key, value): if key == "method": if (value is not None and value not in self.available_methods()): raise AerError( - "Invalid simulation method {}. Available methods" - " are: {}".format(value, self.available_methods())) + f"Invalid simulation method {value}. Available methods" + f" are: {self.available_methods()}") self._set_method_config(value) super().set_option(key, value) if key in ["method", "noise_model", "basis_gates"]: diff --git a/qiskit_aer/backends/aerbackend.py b/qiskit_aer/backends/aerbackend.py index 6cc6cafcc1..501ff50756 100644 --- a/qiskit_aer/backends/aerbackend.py +++ b/qiskit_aer/backends/aerbackend.py @@ -32,7 +32,7 @@ from ..jobs import AerJob, AerJobSet, split_qobj from ..noise.noise_model import NoiseModel, QuantumErrorLocation from ..noise.errors.quantum_error import QuantumChannelInstruction -from .aer_compiler import compile_circuit +from .aer_compiler import compile_circuit, assemble_circuits, generate_aer_config from .backend_utils import format_save_type, circuit_optypes # Logger @@ -145,8 +145,8 @@ def run(self, AerJob: The simulation job. Raises: - AerError: If ``parameter_binds`` is specified with a qobj input or has a - length mismatch with the number of circuits. + TypeError: If ``parameter_binds`` is specified with a qobj input or + has a length mismatch with the number of circuits. Additional Information: * Each parameter binding dictionary is of the form:: @@ -167,6 +167,9 @@ def run(self, Raises: ValueError: if run is not implemented """ + if isinstance(circuits, (QuantumCircuit, Schedule, ScheduleBlock)): + circuits = [circuits] + if isinstance(circuits, (QasmQobj, PulseQobj)): warnings.warn( 'Using a qobj for run() is deprecated as of qiskit-aer 0.9.0' @@ -175,7 +178,7 @@ def run(self, ' `backend.run(circuits, **run_options).', DeprecationWarning, stacklevel=2) if parameter_binds: - raise AerError("Parameter binds can't be used with an input qobj") + raise TypeError("Parameter binds can't be used with an input qobj") # A work around to support both qobj options and run options until # qobj is deprecated is to copy all the set qobj.config fields into # run_options that don't override existing fields. This means set @@ -188,9 +191,68 @@ def run(self, for key, value in circuits.config.__dict__.items(): if key not in run_options and value is not None: run_options[key] = value - qobj = self._assemble(circuits, **run_options) + if 'parameter_binds' in run_options: + parameter_binds = run_options.pop('parameter_binds') + return self._run_qobj(circuits, validate, parameter_binds, **run_options) + + only_circuits = True + only_pulse = True + for circ in circuits: + only_circuits &= isinstance(circ, QuantumCircuit) + only_pulse &= isinstance(circ, (ScheduleBlock, Schedule)) + + if only_circuits and not only_pulse: + if validate: + raise TypeError("bad input to run() function;" + "`validation` argument is only effective for input qobj") + + executor = run_options.get('executor', None) + if executor is None and 'executor' in self.options.__dict__: + executor = self.options.__dict__.get('executor', None) + if executor: + # This path remains for DASK execution to split a qobj insttance + # into sub-qobj instances. This will be replaced with _run_circuits path + # in the near releases + return self._run_qobj(circuits, validate, parameter_binds, **run_options) + else: + return self._run_circuits(circuits, parameter_binds, **run_options) + elif not only_circuits and only_pulse: + return self._run_qobj(circuits, validate, parameter_binds, **run_options) + elif not only_circuits and not only_pulse: + raise TypeError("bad input to run() function;" + "circuits and schedules cannot be mixed in a single run") else: - qobj = self._assemble(circuits, parameter_binds=parameter_binds, **run_options) + raise TypeError("bad input to run() function;" + "circuits must be either circuits or schedules") + + def _run_circuits(self, + circuits, + parameter_binds, + **run_options): + """Run circuits by generating native circuits. + """ + circuits, noise_model = self._compile(circuits, **run_options) + if parameter_binds: + run_options['parameterizations'] = self._convert_binds(circuits, parameter_binds) + config = generate_aer_config(circuits, self.options, **run_options) + + # Submit job + job_id = str(uuid.uuid4()) + aer_job = AerJob(self, job_id, self._execute_circuits_job, + circuits=circuits, noise_model=noise_model, config=config) + aer_job.submit() + + return aer_job + + # pylint: disable=arguments-differ + def _run_qobj(self, + circuits, + validate=False, + parameter_binds=None, + **run_options): + """Run circuits by assembling qobj. + """ + qobj = self._assemble(circuits, parameter_binds=parameter_binds, **run_options) # Optional validation if validate: @@ -215,9 +277,10 @@ def run(self, # Submit job job_id = str(uuid.uuid4()) if isinstance(experiments, list): - aer_job = AerJobSet(self, job_id, self._run, experiments, executor) + aer_job = AerJobSet(self, job_id, self._execute_qobj_job, experiments, executor) else: - aer_job = AerJob(self, job_id, self._run, experiments, executor) + aer_job = AerJob(self, job_id, self._execute_qobj_job, qobj=experiments, + executor=executor) aer_job.submit() # Restore removed executor after submission @@ -289,8 +352,8 @@ def status(self): pending_jobs=0, status_msg='') - def _run(self, qobj, job_id='', format_result=True): - """Run a job""" + def _execute_qobj_job(self, qobj, job_id='', format_result=True): + """Run a qobj job""" # Start timer start = time.time() @@ -308,7 +371,7 @@ def _run(self, qobj, job_id='', format_result=True): metadata_index += 1 # Run simulation - output = self._execute(qobj) + output = self._execute_qobj(qobj) # Recover metadata metadata_index = 0 @@ -353,6 +416,65 @@ def _run(self, qobj, job_id='', format_result=True): return self._format_results(output) return output + def _execute_circuits_job(self, circuits, noise_model, config, job_id='', + format_result=True): + """Run a job""" + # Start timer + start = time.time() + + # Take metadata from headers of experiments to work around JSON serialization error + metadata_list = [] + for idx, circ in enumerate(circuits): + if circ.metadata: + metadata = circ.metadata + metadata_list.append(metadata) + circ.metadata = {} + circ.metadata["metadata_index"] = idx + else: + metadata_list.append(None) + + # Run simulation + aer_circuits = assemble_circuits(circuits) + output = self._execute_circuits(aer_circuits, noise_model, config) + + # Validate output + if not isinstance(output, dict): + logger.error("%s: simulation failed.", self.name()) + if output: + logger.error('Output: %s', output) + raise AerError( + "simulation terminated without returning valid output.") + + # Format results + output["job_id"] = job_id + output["date"] = datetime.datetime.now().isoformat() + output["backend_name"] = self.name() + output["backend_version"] = self.configuration().backend_version + + # Push metadata to experiment headers + for result in output["results"]: + if ("header" in result and + "metadata" in result["header"] and result["header"]["metadata"] and + "metadata_index" in result["header"]["metadata"]): + metadata_index = result["header"]["metadata"]["metadata_index"] + result["header"]["metadata"] = metadata_list[metadata_index] + + for circ, metadata in zip(circuits, metadata_list): + circ.metadata = metadata + + # Add execution time + output["time_taken"] = time.time() - start + + # Display warning if simulation failed + if not output.get("success", False): + msg = "Simulation failed" + if "status" in output: + msg += f" and returned the following error message:\n{output['status']}" + logger.warning(msg) + if format_result: + return self._format_results(output) + return output + @staticmethod def _format_results(output): """Format C++ simulator output for constructing Result""" @@ -366,26 +488,36 @@ def _format_results(output): data[key] = format_save_type(val, save_types[key], save_subtypes[key]) return Result.from_dict(output) + def _compile(self, circuits, **run_options): + """Compile circuits and noise model""" + if isinstance(circuits, (QuantumCircuit, Schedule, ScheduleBlock)): + circuits = [circuits] + optypes = [circuit_optypes(circ) for circ in circuits] + + # Compile Qasm3 instructions + circuits, optypes = compile_circuit( + circuits, + basis_gates=self.configuration().basis_gates, + optypes=optypes) + + # run option noise model + circuits, noise_model, run_options = self._assemble_noise_model( + circuits, optypes, **run_options) + + return circuits, noise_model + def _assemble(self, circuits, parameter_binds=None, **run_options): """Assemble one or more Qobj for running on the simulator""" + if isinstance(circuits, (QasmQobj, PulseQobj)): qobj = circuits else: - # Generate optypes for circuit - # Generate opsets of instructions - if isinstance(circuits, (QuantumCircuit, Schedule, ScheduleBlock)): - circuits = [circuits] - optypes = [circuit_optypes(circ) for circ in circuits] - - # Compile Qasm3 instructions - circuits, optypes = compile_circuit( - circuits, - basis_gates=self.configuration().basis_gates, - optypes=optypes) - - # run option noise model - circuits, optypes, run_options = self._assemble_noise_model( - circuits, optypes, **run_options) + # compile and insert noise injection points + circuits, noise_model = self._compile(circuits, **run_options) + + # If noise model exists, add it to the run options + if noise_model: + run_options['noise_model'] = noise_model if parameter_binds: # Handle parameter binding @@ -405,12 +537,6 @@ def _assemble(self, circuits, parameter_binds=None, **run_options): else: qobj = assemble(circuits, backend=self) - # Add optypes to qobj - # We convert to strings to avoid pybinding of types - qobj.config.optypes = [ - set(i.__name__ for i in optype) if optype else set() - for optype in optypes] - # Add options for key, val in self.options.__dict__.items(): if val is not None: @@ -424,11 +550,8 @@ def _assemble(self, circuits, parameter_binds=None, **run_options): def _assemble_noise_model(self, circuits, optypes, **run_options): """Move quantum error instructions from circuits to noise model""" - if isinstance(circuits, (QuantumCircuit, Schedule, ScheduleBlock)): - run_circuits = [circuits] - else: - # Make a shallow copy so we can modify list elements if required - run_circuits = copy.copy(circuits) + # Make a shallow copy so we can modify list elements if required + run_circuits = copy.copy(circuits) # Flag for if we need to make a deep copy of the noise model # This avoids unnecessarily copying the noise model for circuits @@ -490,12 +613,8 @@ def _assemble_noise_model(self, circuits, optypes, **run_options): run_circuits[idx] = new_circ optypes[idx].discard(QuantumChannelInstruction) - # If we modified the existing noise model, add it to the run options - if updated_noise: - run_options['noise_model'] = noise_model - # Return the possibly updated circuits and noise model - return run_circuits, optypes, run_options + return run_circuits, noise_model, run_options def _get_executor(self, **run_options): """Get the executor""" @@ -505,7 +624,7 @@ def _get_executor(self, **run_options): return getattr(self._options, 'executor', None) @abstractmethod - def _execute(self, qobj): + def _execute_qobj(self, qobj): """Execute a qobj on the backend. Args: @@ -516,6 +635,20 @@ def _execute(self, qobj): """ pass + @abstractmethod + def _execute_circuits(self, aer_circuits, noise_model, config): + """Execute aer circuits on the backend. + + Args: + aer_circuits (List of AerCircuit): simulator input. + noise_model (NoiseModel): noise model + config (Dict): configuration for simulation + + Returns: + dict: return a dictionary of results. + """ + pass + def _validate(self, qobj): """Validate the qobj for the backend""" pass @@ -545,7 +678,7 @@ def set_option(self, key, value): self._set_defaults_option(key, value) else: if not hasattr(self._options, key): - raise AerError("Invalid option %s" % key) + raise AerError(f"Invalid option {key}") if value is not None: # Only add an option if its value is not None setattr(self._options, key, value) diff --git a/qiskit_aer/backends/backend_utils.py b/qiskit_aer/backends/backend_utils.py index dcbc6a50f9..c85049e6f2 100644 --- a/qiskit_aer/backends/backend_utils.py +++ b/qiskit_aer/backends/backend_utils.py @@ -25,7 +25,6 @@ from .compatibility import ( Statevector, DensityMatrix, StabilizerState, Operator, SuperOp) - # Available system memory SYSTEM_MEMORY_GB = local_hardware_info()['memory'] @@ -117,16 +116,27 @@ BASIS_GATES['tensor_network'])) -def cpp_execute(controller, qobj): +def cpp_execute_qobj(controller, qobj): """Execute qobj on C++ controller wrapper""" # Location where we put external libraries that will be # loaded at runtime by the simulator extension qobj.config.library_dir = LIBRARY_DIR - return controller(qobj) +def cpp_execute_circuits(controller, aer_circuits, noise_model, config): + """Execute aer circuits on C++ controller wrapper""" + + # Location where we put external libraries that will be + # loaded at runtime by the simulator extension + config.library_dir = LIBRARY_DIR + + noise_model = noise_model.to_dict(serializable=True) if noise_model else {} + + return controller.execute(aer_circuits, noise_model, config) + + def available_methods(controller, methods, devices): """Check available simulation methods by running a dummy circuit.""" # Test methods are available using the controller @@ -142,7 +152,7 @@ def available_methods(controller, methods, devices): shots=1, method=method, device=device) - result = cpp_execute(controller, qobj) + result = cpp_execute_qobj(controller, qobj) if result.get('success', False): valid_methods.append(method) return tuple(valid_methods) @@ -161,7 +171,7 @@ def available_devices(controller, devices): shots=1, method="statevector", device=device) - result = cpp_execute(controller, qobj) + result = cpp_execute_qobj(controller, qobj) if result.get('success', False): valid_devices.append(device) return tuple(valid_devices) @@ -185,6 +195,19 @@ def save_inst(num_qubits): return qobj +def add_final_save_op(aer_circuits, state): + """Add final save state op to all experiments in a qobj.""" + + for aer_circuit in aer_circuits: + num_qubits = aer_circuit.num_qubits + aer_circuit.save_state(list(range(num_qubits)), + f"save_{state}", + "single", + state) + + return aer_circuits + + def map_legacy_method_options(qobj): """Map legacy method names of qasm simulator to aer simulator options""" method = getattr(qobj.config, "method", None) @@ -193,6 +216,14 @@ def map_legacy_method_options(qobj): return qobj +def map_legacy_method_config(config): + """Map legacy method names of qasm simulator to aer simulator options""" + method = config.method + if method in LEGACY_METHOD_MAP: + config.method, config.device = LEGACY_METHOD_MAP[method] + return config + + def format_save_type(data, save_type, save_subtype): """Format raw simulator result data based on save type.""" init_fns = { diff --git a/qiskit_aer/backends/pulse_simulator.py b/qiskit_aer/backends/pulse_simulator.py index 49d1d71764..b3be282793 100644 --- a/qiskit_aer/backends/pulse_simulator.py +++ b/qiskit_aer/backends/pulse_simulator.py @@ -280,7 +280,7 @@ def from_backend(cls, backend, **options): **options) return sim - def _execute(self, qobj): + def _execute_qobj(self, qobj): """Execute a qobj on the backend. Args: @@ -292,6 +292,11 @@ def _execute(self, qobj): qobj.config.qubit_freq_est = self.defaults().qubit_freq_est return pulse_controller(qobj) + def _execute_circuits(self, aer_circuits, noise_model, config): + """Execute circuits on the backend. + """ + raise TypeError("pulse simulator does not support circuit execution") + def set_option(self, key, value): """Set pulse simulation options and update backend.""" if key == 'meas_levels': diff --git a/qiskit_aer/backends/qasm_simulator.py b/qiskit_aer/backends/qasm_simulator.py index a0dec10856..4a17dd8f37 100644 --- a/qiskit_aer/backends/qasm_simulator.py +++ b/qiskit_aer/backends/qasm_simulator.py @@ -23,10 +23,13 @@ from ..version import __version__ from ..aererror import AerError from .aerbackend import AerBackend -from .backend_utils import (cpp_execute, available_methods, +from .backend_utils import (cpp_execute_qobj, + cpp_execute_circuits, + available_methods, MAX_QUBITS_STATEVECTOR, LEGACY_METHOD_MAP, - map_legacy_method_options) + map_legacy_method_options, + map_legacy_method_config) # pylint: disable=import-error, no-name-in-module from .controller_wrappers import aer_controller_execute @@ -392,11 +395,11 @@ def __repr__(self): method = getattr(self.options, 'method', None) if method not in [None, 'automatic']: - display += ",\n{}method='{}'".format(pad, method) + display += f",\n{pad}method='{method}'" noise_model = getattr(self.options, 'noise_model', None) if noise_model is not None and not noise_model.is_ideal(): - display += ',\n{}noise_model={})'.format(pad, repr(noise_model)) + display += f',\n{pad}noise_model={repr(noise_model)})' display += ")" return display @@ -468,7 +471,7 @@ def from_backend(cls, backend, **options): # Customize configuration name name = configuration.backend_name - configuration.backend_name = 'qasm_simulator({})'.format(name) + configuration.backend_name = f'qasm_simulator({name})' # Use automatic noise model if none is provided if 'noise_model' not in options: @@ -505,7 +508,7 @@ def available_devices(self): """Return the available simulation methods.""" return copy.copy(self._AVAILABLE_DEVICES) - def _execute(self, qobj): + def _execute_qobj(self, qobj): """Execute a qobj on the backend. Args: @@ -515,7 +518,13 @@ def _execute(self, qobj): dict: return a dictionary of results. """ qobj = map_legacy_method_options(qobj) - return cpp_execute(self._controller, qobj) + return cpp_execute_qobj(self._controller, qobj) + + def _execute_circuits(self, aer_circuits, noise_model, config): + """Execute circuits on the backend. + """ + config = map_legacy_method_config(config) + return cpp_execute_circuits(self._controller, aer_circuits, noise_model, config) def set_option(self, key, value): if key == "custom_instructions": @@ -527,8 +536,8 @@ def set_option(self, key, value): self.set_option("device", device) if (value is not None and value not in self.available_methods()): raise AerError( - "Invalid simulation method {}. Available methods" - " are: {}".format(value, self.available_methods())) + f"Invalid simulation method {value}. Available methods" + f" are: {self.available_methods()}") self._set_method_config(value) super().set_option(key, value) if key in ["method", "noise_model", "basis_gates"]: diff --git a/qiskit_aer/backends/statevector_simulator.py b/qiskit_aer/backends/statevector_simulator.py index 100cfb7b57..93564bb723 100644 --- a/qiskit_aer/backends/statevector_simulator.py +++ b/qiskit_aer/backends/statevector_simulator.py @@ -23,11 +23,15 @@ from ..aererror import AerError from ..version import __version__ from .aerbackend import AerBackend -from .backend_utils import (cpp_execute, available_devices, +from .backend_utils import (cpp_execute_qobj, available_devices, MAX_QUBITS_STATEVECTOR, LEGACY_METHOD_MAP, add_final_save_instruction, - map_legacy_method_options) + cpp_execute_circuits, + map_legacy_method_options, + map_legacy_method_config, + add_final_save_op, + ) # pylint: disable=import-error, no-name-in-module from .controller_wrappers import aer_controller_execute @@ -252,7 +256,7 @@ def available_devices(self): """Return the available simulation methods.""" return copy.copy(self._AVAILABLE_DEVICES) - def _execute(self, qobj): + def _execute_qobj(self, qobj): """Execute a qobj on the backend. Args: @@ -265,7 +269,14 @@ def _execute(self, qobj): qobj = copy.deepcopy(qobj) qobj = add_final_save_instruction(qobj, "statevector") qobj = map_legacy_method_options(qobj) - return cpp_execute(self._controller, qobj) + return cpp_execute_qobj(self._controller, qobj) + + def _execute_circuits(self, aer_circuits, noise_model, config): + """Execute circuits on the backend. + """ + config = map_legacy_method_config(config) + aer_circuits = add_final_save_op(aer_circuits, "statevector") + return cpp_execute_circuits(self._controller, aer_circuits, noise_model, config) def _validate(self, qobj): """Semantic validations of the qobj which cannot be done via schemas. @@ -276,15 +287,14 @@ def _validate(self, qobj): """ name = self.name() if getattr(qobj.config, 'noise_model', None) is not None: - raise AerError("{} does not support noise.".format(name)) + raise AerError(f"{name} does not support noise.") n_qubits = qobj.config.n_qubits max_qubits = self.configuration().n_qubits if n_qubits > max_qubits: raise AerError( - 'Number of qubits ({}) is greater than max ({}) for "{}" with {} GB system memory.' - .format(n_qubits, max_qubits, name, - int(local_hardware_info()['memory']))) + f'Number of qubits ({n_qubits}) is greater than max ({max_qubits}) ' + f'for "{name}" with {int(local_hardware_info()["memory"])} GB system memory.') if qobj.config.shots != 1: logger.info('"%s" only supports 1 shot. Setting shots=1.', name) diff --git a/qiskit_aer/backends/unitary_simulator.py b/qiskit_aer/backends/unitary_simulator.py index 2db5880aa9..7971391f3d 100644 --- a/qiskit_aer/backends/unitary_simulator.py +++ b/qiskit_aer/backends/unitary_simulator.py @@ -24,11 +24,15 @@ from ..aererror import AerError from ..version import __version__ from .aerbackend import AerBackend -from .backend_utils import (cpp_execute, available_devices, +from .backend_utils import (cpp_execute_qobj, + cpp_execute_circuits, + available_devices, MAX_QUBITS_STATEVECTOR, LEGACY_METHOD_MAP, add_final_save_instruction, - map_legacy_method_options) + map_legacy_method_options, + add_final_save_op, + map_legacy_method_config) # pylint: disable=import-error, no-name-in-module from .controller_wrappers import aer_controller_execute @@ -251,7 +255,7 @@ def available_devices(self): """Return the available simulation methods.""" return copy.copy(self._AVAILABLE_DEVICES) - def _execute(self, qobj): + def _execute_qobj(self, qobj): """Execute a qobj on the backend. Args: @@ -264,7 +268,14 @@ def _execute(self, qobj): qobj = copy.deepcopy(qobj) qobj = add_final_save_instruction(qobj, "unitary") qobj = map_legacy_method_options(qobj) - return cpp_execute(self._controller, qobj) + return cpp_execute_qobj(self._controller, qobj) + + def _execute_circuits(self, aer_circuits, noise_model, config): + """Execute circuits on the backend. + """ + config = map_legacy_method_config(config) + aer_circuits = add_final_save_op(aer_circuits, "unitary") + return cpp_execute_circuits(self._controller, aer_circuits, noise_model, config) def _validate(self, qobj): """Semantic validations of the qobj which cannot be done via schemas. @@ -275,15 +286,15 @@ def _validate(self, qobj): """ name = self.name() if getattr(qobj.config, 'noise_model', None) is not None: - raise AerError("{} does not support noise.".format(name)) + raise AerError(f"{name} does not support noise.") n_qubits = qobj.config.n_qubits max_qubits = self.configuration().n_qubits if n_qubits > max_qubits: raise AerError( - 'Number of qubits ({}) is greater than max ({}) for "{}" with {} GB system memory.' - .format(n_qubits, max_qubits, name, - int(local_hardware_info()['memory']))) + f'Number of qubits ({n_qubits}) is greater than ' + f'max ({max_qubits}) for "{name}" with ' + f"{int(local_hardware_info()['memory'])} GB system memory.") if qobj.config.shots != 1: logger.info('"%s" only supports 1 shot. Setting shots=1.', name) qobj.config.shots = 1 @@ -297,5 +308,4 @@ def _validate(self, qobj): for operation in experiment.instructions: if operation.name in ['measure', 'reset']: raise AerError( - 'Unsupported {} instruction {} in circuit {}'.format( - name, operation.name, exp_name)) + f'Unsupported {name} instruction {operation.name} in circuit {exp_name}') diff --git a/qiskit_aer/backends/wrappers/aer_circuit_binding.hpp b/qiskit_aer/backends/wrappers/aer_circuit_binding.hpp new file mode 100644 index 0000000000..ffd7fe37a9 --- /dev/null +++ b/qiskit_aer/backends/wrappers/aer_circuit_binding.hpp @@ -0,0 +1,104 @@ +/** + * This code is part of Qiskit. + * + * (C) Copyright IBM 2023. + * + * This code is licensed under the Apache License, Version 2.0. You may + * obtain a copy of this license in the LICENSE.txt file in the root directory + * of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. + * + * Any modifications or derivative works of this code must retain this + * copyright notice, and modified files need to carry a notice indicating + * that they have been altered from the originals. + */ + +#ifndef _aer_circuit_binding_hpp_ +#define _aer_circuit_binding_hpp_ + +#include "misc/warnings.hpp" +DISABLE_WARNING_PUSH +#include +DISABLE_WARNING_POP +#if defined(_MSC_VER) + #undef snprintf +#endif + +#include + +#include "framework/matrix.hpp" +#include "framework/python_parser.hpp" +#include "framework/pybind_json.hpp" +#include "framework/pybind_casts.hpp" + +#include "framework/types.hpp" +#include "framework/results/pybind_result.hpp" + +#include "framework/circuit.hpp" + +namespace py = pybind11; +using namespace AER; + +template +void bind_aer_circuit(MODULE m) { + py::class_ aer_circuit(m, "AerCircuit"); + aer_circuit.def(py::init()); + aer_circuit.def("__repr__", [](const Circuit &circ) { + std::stringstream ss; + ss << "Circuit(" + << "qubit=" << circ.num_qubits + << ", num_memory=" << circ.num_memory + << ", num_registers=" << circ.num_registers; + + ss << ", ops={"; + for (auto i = 0; i < circ.ops.size(); ++i) + if (i == 0) + ss << circ.ops[i]; + else + ss << "," << circ.ops[i]; + + ss << "}" + << ", shots=" << circ.shots + << ", seed=" << circ.seed + << ", global_phase_angle=" << circ.global_phase_angle + ; + ss << ")"; + return ss.str(); + }); + + aer_circuit.def_readwrite("shots", &Circuit::shots); + aer_circuit.def_readwrite("num_qubits", &Circuit::num_qubits); + aer_circuit.def_readwrite("num_memory", &Circuit::num_memory); + aer_circuit.def_readwrite("seed", &Circuit::seed); + aer_circuit.def_readwrite("ops", &Circuit::ops); + aer_circuit.def_readwrite("global_phase_angle", &Circuit::global_phase_angle); + aer_circuit.def("set_header", [aer_circuit](Circuit &circ, + const py::handle& header) { + circ.header = header; + }); + aer_circuit.def("bfunc", &Circuit::bfunc); + aer_circuit.def("gate", &Circuit::gate); + aer_circuit.def("diagonal", &Circuit::diagonal); + aer_circuit.def("unitary", &Circuit::unitary); + aer_circuit.def("roerror", &Circuit::roerror); + aer_circuit.def("multiplexer", &Circuit::multiplexer); + aer_circuit.def("kraus", &Circuit::kraus); + aer_circuit.def("superop", &Circuit::superop); + aer_circuit.def("save_state", &Circuit::save_state); + aer_circuit.def("save_amplitudes", &Circuit::save_amplitudes); + aer_circuit.def("save_expval", &Circuit::save_expval); + aer_circuit.def("initialize", &Circuit::initialize); + aer_circuit.def("set_statevector", &Circuit::set_statevector); + aer_circuit.def("set_density_matrix", &Circuit::set_density_matrix); + aer_circuit.def("set_unitary", &Circuit::set_unitary); + aer_circuit.def("set_superop", &Circuit::set_superop); + aer_circuit.def("set_matrix_product_state", &Circuit::set_matrix_product_state); + aer_circuit.def("set_clifford", &Circuit::set_clifford); + aer_circuit.def("jump", &Circuit::jump); + aer_circuit.def("mark", &Circuit::mark); + aer_circuit.def("measure", &Circuit::measure); + aer_circuit.def("reset", &Circuit::reset); + aer_circuit.def("set_qerror_loc", &Circuit::set_qerror_loc); + +} + +#endif \ No newline at end of file diff --git a/qiskit_aer/backends/wrappers/aer_controller_binding.hpp b/qiskit_aer/backends/wrappers/aer_controller_binding.hpp new file mode 100644 index 0000000000..a04a6a8430 --- /dev/null +++ b/qiskit_aer/backends/wrappers/aer_controller_binding.hpp @@ -0,0 +1,458 @@ +/** + * This code is part of Qiskit. + * + * (C) Copyright IBM 2023. + * + * This code is licensed under the Apache License, Version 2.0. You may + * obtain a copy of this license in the LICENSE.txt file in the root directory + * of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. + * + * Any modifications or derivative works of this code must retain this + * copyright notice, and modified files need to carry a notice indicating + * that they have been altered from the originals. + */ + +#ifndef _aer_controller_binding_hpp_ +#define _aer_controller_binding_hpp_ + +#include "misc/warnings.hpp" +DISABLE_WARNING_PUSH +#include +DISABLE_WARNING_POP +#if defined(_MSC_VER) + #undef snprintf +#endif + +#include + +#include "framework/matrix.hpp" +#include "framework/python_parser.hpp" +#include "framework/pybind_casts.hpp" +#include "framework/types.hpp" +#include "framework/results/pybind_result.hpp" + +#include "controllers/aer_controller.hpp" + +#include "controllers/controller_execute.hpp" + +namespace py = pybind11; +using namespace AER; + +template +class ControllerExecutor { +public: + ControllerExecutor() = default; + py::object operator()(const py::handle &qobj) { +#ifdef TEST_JSON // Convert input qobj to json to test standalone data reading + return AerToPy::to_python(controller_execute(json_t(qobj))); +#else + return AerToPy::to_python(controller_execute(qobj)); +#endif + } + + py::object execute(std::vector &circuits, Noise::NoiseModel &noise_model, AER::Config& config) const { + return AerToPy::to_python(controller_execute(circuits, noise_model, config)); + } +}; + +template +py::tuple write_value(size_t index, const optional &v) { + return py::make_tuple(v.has_value(), v.value()); +} + +template +T write_value(size_t index, const T &v) { + return v; +} + +template +void read_value(const py::tuple &t, size_t index, optional &v) { + if (t[index].cast()[0].cast()) + v.value(t[index].cast()[1].cast()); +} + +template +void read_value(const py::tuple &t, size_t index, T &v) { + v = t[index].cast(); +} + +template +void bind_aer_controller(MODULE m) { + py::class_ > aer_ctrl (m, "aer_controller_execute"); + aer_ctrl.def(py::init<>()); + aer_ctrl.def("__call__", &ControllerExecutor::operator()); + aer_ctrl.def("__reduce__", [aer_ctrl](const ControllerExecutor &self) { + return py::make_tuple(aer_ctrl, py::tuple()); + }); + aer_ctrl.def("execute", [aer_ctrl](ControllerExecutor &self, + std::vector &circuits, + py::object noise_model, + AER::Config &config) { + + Noise::NoiseModel noise_model_native; + if (noise_model) + noise_model_native.load_from_json(noise_model); + + return self.execute(circuits, noise_model_native, config); + }); + + py::class_ aer_config(m, "AerConfig"); + aer_config.def(py::init()); + aer_config.def_readwrite("shots", &Config::shots); + aer_config.def_readwrite("method", &Config::method); + aer_config.def_readwrite("device", &Config::device); + aer_config.def_readwrite("precision", &Config::precision); + aer_config.def_readwrite("enable_truncation", &Config::enable_truncation); + aer_config.def_readwrite("zero_threshold", &Config::zero_threshold); + aer_config.def_readwrite("validation_threshold", &Config::validation_threshold); + aer_config.def_property("max_parallel_threads", + [](const Config &config) { return config.max_parallel_threads.val;}, + [](Config &config, uint_t val) { config.max_parallel_threads.value(val);}); + aer_config.def_property("max_parallel_experiments", + [](const Config &config) { return config.max_parallel_experiments.val;}, + [](Config &config, uint_t val) { config.max_parallel_experiments.value(val);}); + aer_config.def_property("max_parallel_shots", + [](const Config &config) { return config.max_parallel_shots.val;}, + [](Config &config, uint_t val) { config.max_parallel_shots.value(val);}); + aer_config.def_property("max_memory_mb", + [](const Config &config) { return config.max_memory_mb.val;}, + [](Config &config, uint_t val) { config.max_memory_mb.value(val);}); + aer_config.def_readwrite("fusion_enable", &Config::fusion_enable); + aer_config.def_readwrite("fusion_verbose", &Config::fusion_verbose); + aer_config.def_property("fusion_max_qubit", + [](const Config &config) { return config.fusion_max_qubit.val;}, + [](Config &config, uint_t val) { config.fusion_max_qubit.value(val);}); + aer_config.def_property("fusion_threshold", + [](const Config &config) { return config.fusion_threshold.val;}, + [](Config &config, uint_t val) { config.fusion_threshold.value(val);}); + aer_config.def_property("accept_distributed_results", + [](const Config &config) { return config.accept_distributed_results.val;}, + [](Config &config, bool val) { config.accept_distributed_results.value(val);}); + aer_config.def_property("memory", + [](const Config &config) { return config.memory.val;}, + [](Config &config, bool val) { config.memory.value(val);}); + aer_config.def_property("seed_simulator", + [](const Config &config) { return config.seed_simulator.val;}, + [](Config &config, int_t val) { config.seed_simulator.value(val);}); + // # cuStateVec (cuQuantum) option + aer_config.def_property("cuStateVec_enable", + [](const Config &config) { return config.cuStateVec_enable.val;}, + [](Config &config, bool val) { config.cuStateVec_enable.value(val);}); + // # cache blocking for multi-GPUs/MPI options + aer_config.def_property("blocking_qubits", + [](const Config &config) { return config.blocking_qubits.val;}, + [](Config &config, uint_t val) { config.blocking_qubits.value(val);}); + aer_config.def_readwrite("blocking_enable", &Config::blocking_enable); + aer_config.def_property("chunk_swap_buffer_qubits", + [](const Config &config) { return config.chunk_swap_buffer_qubits.val;}, + [](Config &config, uint_t val) { config.chunk_swap_buffer_qubits.value(val);}); + // # multi-shots optimization options (GPU only) + aer_config.def_readwrite("batched_shots_gpu", &Config::batched_shots_gpu); + aer_config.def_readwrite("batched_shots_gpu_max_qubits", &Config::batched_shots_gpu_max_qubits); + aer_config.def_property("num_threads_per_device", + [](const Config &config) { return config.num_threads_per_device.val;}, + [](Config &config, uint_t val) { config.num_threads_per_device.value(val);}); + // # statevector options + aer_config.def_readwrite("statevector_parallel_threshold", &Config::statevector_parallel_threshold); + aer_config.def_readwrite("statevector_sample_measure_opt", &Config::statevector_sample_measure_opt); + // # stabilizer options + aer_config.def_readwrite("stabilizer_max_snapshot_probabilities", &Config::stabilizer_max_snapshot_probabilities); + // # extended stabilizer options + aer_config.def_readwrite("extended_stabilizer_sampling_method", &Config::extended_stabilizer_sampling_method); + aer_config.def_readwrite("extended_stabilizer_metropolis_mixing_time", &Config::extended_stabilizer_metropolis_mixing_time); + aer_config.def_readwrite("extended_stabilizer_approximation_error", &Config::extended_stabilizer_approximation_error); + aer_config.def_readwrite("extended_stabilizer_norm_estimation_samples", &Config::extended_stabilizer_norm_estimation_samples); + aer_config.def_readwrite("extended_stabilizer_norm_estimation_repetitions", &Config::extended_stabilizer_norm_estimation_repetitions); + aer_config.def_readwrite("extended_stabilizer_parallel_threshold", &Config::extended_stabilizer_parallel_threshold); + aer_config.def_readwrite("extended_stabilizer_probabilities_snapshot_samples", &Config::extended_stabilizer_probabilities_snapshot_samples); + // # MPS options + aer_config.def_readwrite("matrix_product_state_truncation_threshold", &Config::matrix_product_state_truncation_threshold); + aer_config.def_property("matrix_product_state_max_bond_dimension", + [](const Config &config) { return config.matrix_product_state_max_bond_dimension.val;}, + [](Config &config, uint_t val) { config.matrix_product_state_max_bond_dimension.value(val);}); + aer_config.def_readwrite("mps_sample_measure_algorithm", &Config::mps_sample_measure_algorithm); + aer_config.def_readwrite("mps_log_data", &Config::mps_log_data); + aer_config.def_readwrite("mps_swap_direction", &Config::mps_swap_direction); + aer_config.def_readwrite("chop_threshold", &Config::chop_threshold); + aer_config.def_readwrite("mps_parallel_threshold", &Config::mps_parallel_threshold); + aer_config.def_readwrite("mps_omp_threads", &Config::mps_omp_threads); + // # tensor network options + aer_config.def_readwrite("tensor_network_num_sampling_qubits", &Config::tensor_network_num_sampling_qubits); + aer_config.def_readwrite("use_cuTensorNet_autotuning", &Config::use_cuTensorNet_autotuning); + + // system configurations + aer_config.def_readwrite("library_dir", &Config::library_dir); + aer_config.def_readwrite("parameterizations", &Config::param_table); + aer_config.def_property("n_qubits", + [](const Config &config) { return config.n_qubits.val;}, + [](Config &config, uint_t val) { config.n_qubits.value(val);}); + aer_config.def_readwrite("global_phase", &Config::global_phase); + aer_config.def_readwrite("memory_slots", &Config::memory_slots); + aer_config.def_property("_parallel_experiments", + [](const Config &config) { return config._parallel_experiments.val;}, + [](Config &config, uint_t val) { config._parallel_experiments.value(val);}); + aer_config.def_property("_parallel_shots", + [](const Config &config) { return config._parallel_shots.val;}, + [](Config &config, uint_t val) { config._parallel_shots.value(val);}); + aer_config.def_property("_parallel_state_update", + [](const Config &config) { return config._parallel_state_update.val;}, + [](Config &config, uint_t val) { config._parallel_state_update.value(val);}); + aer_config.def_property("fusion_allow_kraus", + [](const Config &config) { return config.fusion_allow_kraus.val;}, + [](Config &config, bool val) { config.fusion_allow_kraus.value(val);}); + aer_config.def_property("fusion_allow_superop", + [](const Config &config) { return config.fusion_allow_superop.val;}, + [](Config &config, bool val) { config.fusion_allow_superop.value(val);}); + aer_config.def_property("fusion_parallelization_threshold", + [](const Config &config) { return config.fusion_parallelization_threshold.val;}, + [](Config &config, uint_t val) { config.fusion_parallelization_threshold.value(val);}); + aer_config.def_property("_fusion_enable_n_qubits", + [](const Config &config) { return config._fusion_enable_n_qubits.val;}, + [](Config &config, bool val) { config._fusion_enable_n_qubits.value(val);}); + aer_config.def_property("_fusion_enable_n_qubits_1", + [](const Config &config) { return config._fusion_enable_n_qubits_1.val;}, + [](Config &config, uint_t val) { config._fusion_enable_n_qubits_1.value(val);}); + aer_config.def_property("_fusion_enable_n_qubits_2", + [](const Config &config) { return config._fusion_enable_n_qubits_2.val;}, + [](Config &config, uint_t val) { config._fusion_enable_n_qubits_2.value(val);}); + aer_config.def_property("_fusion_enable_n_qubits_3", + [](const Config &config) { return config._fusion_enable_n_qubits_3.val;}, + [](Config &config, uint_t val) { config._fusion_enable_n_qubits_3.value(val);}); + aer_config.def_property("_fusion_enable_n_qubits_4", + [](const Config &config) { return config._fusion_enable_n_qubits_4.val;}, + [](Config &config, uint_t val) { config._fusion_enable_n_qubits_4.value(val);}); + aer_config.def_property("_fusion_enable_n_qubits_5", + [](const Config &config) { return config._fusion_enable_n_qubits_5.val;}, + [](Config &config, uint_t val) { config._fusion_enable_n_qubits_5.value(val);}); + aer_config.def_property("_fusion_enable_diagonal", + [](const Config &config) { return config._fusion_enable_diagonal.val;}, + [](Config &config, uint_t val) { config._fusion_enable_diagonal.value(val);}); + aer_config.def_property("_fusion_min_qubit", + [](const Config &config) { return config._fusion_min_qubit.val;}, + [](Config &config, uint_t val) { config._fusion_min_qubit.value(val);}); + aer_config.def_property("fusion_cost_factor", + [](const Config &config) { return config.fusion_cost_factor.val;}, + [](Config &config, double val) { config.fusion_cost_factor.value(val);}); + aer_config.def_property("_fusion_enable_cost_based", + [](const Config &config) { return config._fusion_enable_cost_based.val;}, + [](Config &config, bool val) { config._fusion_enable_cost_based.value(val);}); + aer_config.def_property("_fusion_cost_1", + [](const Config &config) { return config._fusion_cost_1.val;}, + [](Config &config, uint_t val) { config._fusion_cost_1.value(val);}); + aer_config.def_property("_fusion_cost_2", + [](const Config &config) { return config._fusion_cost_2.val;}, + [](Config &config, uint_t val) { config._fusion_cost_2.value(val);}); + aer_config.def_property("_fusion_cost_3", + [](const Config &config) { return config._fusion_cost_3.val;}, + [](Config &config, uint_t val) { config._fusion_cost_3.value(val);}); + aer_config.def_property("_fusion_cost_4", + [](const Config &config) { return config._fusion_cost_4.val;}, + [](Config &config, uint_t val) { config._fusion_cost_4.value(val);}); + aer_config.def_property("_fusion_cost_5", + [](const Config &config) { return config._fusion_cost_5.val;}, + [](Config &config, uint_t val) { config._fusion_cost_5.value(val);}); + aer_config.def_property("_fusion_cost_6", + [](const Config &config) { return config._fusion_cost_6.val;}, + [](Config &config, uint_t val) { config._fusion_cost_6.value(val);}); + aer_config.def_property("_fusion_cost_7", + [](const Config &config) { return config._fusion_cost_7.val;}, + [](Config &config, uint_t val) { config._fusion_cost_7.value(val);}); + aer_config.def_property("_fusion_cost_8", + [](const Config &config) { return config._fusion_cost_8.val;}, + [](Config &config, uint_t val) { config._fusion_cost_8.value(val);}); + aer_config.def_property("_fusion_cost_9", + [](const Config &config) { return config._fusion_cost_9.val;}, + [](Config &config, uint_t val) { config._fusion_cost_9.value(val);}); + aer_config.def_property("_fusion_cost_10", + [](const Config &config) { return config._fusion_cost_10.val;}, + [](Config &config, uint_t val) { config._fusion_cost_10.value(val);}); + + aer_config.def_property("superoperator_parallel_threshold", + [](const Config &config) { return config.superoperator_parallel_threshold.val;}, + [](Config &config, uint_t val) { config.superoperator_parallel_threshold.value(val);}); + aer_config.def_property("unitary_parallel_threshold", + [](const Config &config) { return config.unitary_parallel_threshold.val;}, + [](Config &config, uint_t val) { config.unitary_parallel_threshold.value(val);}); + aer_config.def_property("memory_blocking_bits", + [](const Config &config) { return config.memory_blocking_bits.val;}, + [](Config &config, uint_t val) { config.memory_blocking_bits.value(val);}); + aer_config.def_property("extended_stabilizer_norm_estimation_default_samples", + [](const Config &config) { return config.extended_stabilizer_norm_estimation_default_samples.val;}, + [](Config &config, uint_t val) { config.extended_stabilizer_norm_estimation_default_samples.value(val);}); + + aer_config.def(py::pickle( + [](const AER::Config &config) { + return py::make_tuple( + write_value(0, config.shots), + write_value(1, config.method), + write_value(2, config.device), + write_value(3, config.precision), + write_value(4, config.enable_truncation), + write_value(5, config.zero_threshold), + write_value(6, config.validation_threshold), + write_value(7, config.max_parallel_threads), + write_value(8, config.max_parallel_experiments), + write_value(9, config.max_parallel_shots), + write_value(10, config.max_memory_mb), + write_value(11, config.fusion_enable), + write_value(12, config.fusion_verbose), + write_value(13, config.fusion_max_qubit), + write_value(14, config.fusion_threshold), + write_value(15, config.accept_distributed_results), + write_value(16, config.memory), + write_value(17, config.seed_simulator), + write_value(18, config.cuStateVec_enable), + write_value(19, config.blocking_qubits), + write_value(20, config.blocking_enable), + write_value(21, config.chunk_swap_buffer_qubits), + write_value(22, config.batched_shots_gpu), + write_value(23, config.batched_shots_gpu_max_qubits), + write_value(24, config.num_threads_per_device), + write_value(25, config.statevector_parallel_threshold), + write_value(26, config.statevector_sample_measure_opt), + write_value(27, config.stabilizer_max_snapshot_probabilities), + write_value(28, config.extended_stabilizer_sampling_method), + write_value(29, config.extended_stabilizer_metropolis_mixing_time), + write_value(20, config.extended_stabilizer_approximation_error), + write_value(31, config.extended_stabilizer_norm_estimation_samples), + write_value(32, config.extended_stabilizer_norm_estimation_repetitions), + write_value(33, config.extended_stabilizer_parallel_threshold), + write_value(34, config.extended_stabilizer_probabilities_snapshot_samples), + write_value(35, config.matrix_product_state_truncation_threshold), + write_value(36, config.matrix_product_state_max_bond_dimension), + write_value(37, config.mps_sample_measure_algorithm), + write_value(38, config.mps_log_data), + write_value(39, config.mps_swap_direction), + write_value(30, config.chop_threshold), + write_value(41, config.mps_parallel_threshold), + write_value(42, config.mps_omp_threads), + write_value(43, config.tensor_network_num_sampling_qubits), + write_value(44, config.use_cuTensorNet_autotuning), + write_value(45, config.library_dir), + write_value(46, config.param_table), + write_value(47, config.n_qubits), + write_value(48, config.global_phase), + write_value(49, config.memory_slots), + write_value(50, config._parallel_experiments), + write_value(51, config._parallel_shots), + write_value(52, config._parallel_state_update), + write_value(53, config.fusion_allow_kraus), + write_value(54, config.fusion_allow_superop), + write_value(55, config.fusion_parallelization_threshold), + write_value(56, config._fusion_enable_n_qubits), + write_value(57, config._fusion_enable_n_qubits_1), + write_value(58, config._fusion_enable_n_qubits_2), + write_value(59, config._fusion_enable_n_qubits_3), + write_value(60, config._fusion_enable_n_qubits_4), + write_value(61, config._fusion_enable_n_qubits_5), + write_value(62, config._fusion_enable_diagonal), + write_value(63, config._fusion_min_qubit), + write_value(64, config.fusion_cost_factor), + write_value(65, config._fusion_enable_cost_based), + write_value(66, config._fusion_cost_1), + write_value(67, config._fusion_cost_2), + write_value(68, config._fusion_cost_3), + write_value(69, config._fusion_cost_4), + write_value(70, config._fusion_cost_5), + write_value(71, config._fusion_cost_6), + write_value(72, config._fusion_cost_7), + write_value(73, config._fusion_cost_8), + write_value(74, config._fusion_cost_9), + write_value(75, config._fusion_cost_10), + + write_value(76, config.superoperator_parallel_threshold), + write_value(77, config.unitary_parallel_threshold), + write_value(78, config.memory_blocking_bits), + write_value(79, config.extended_stabilizer_norm_estimation_default_samples) + ); + }, + [](py::tuple t) { + AER::Config config; + if (t.size() != 79) + throw std::runtime_error("Invalid serialization format."); + + read_value(t, 0, config.shots); + read_value(t, 1, config.method); + read_value(t, 2, config.device); + read_value(t, 3, config.precision); + read_value(t, 4, config.enable_truncation); + read_value(t, 5, config.zero_threshold); + read_value(t, 6, config.validation_threshold); + read_value(t, 7, config.max_parallel_threads); + read_value(t, 8, config.max_parallel_experiments); + read_value(t, 9, config.max_parallel_shots); + read_value(t, 10, config.max_memory_mb); + read_value(t, 11, config.fusion_enable); + read_value(t, 12, config.fusion_verbose); + read_value(t, 13, config.fusion_max_qubit); + read_value(t, 14, config.fusion_threshold); + read_value(t, 15, config.accept_distributed_results); + read_value(t, 16, config.memory); + read_value(t, 17, config.seed_simulator); + read_value(t, 18, config.cuStateVec_enable); + read_value(t, 19, config.blocking_qubits); + read_value(t, 20, config.blocking_enable); + read_value(t, 21, config.chunk_swap_buffer_qubits); + read_value(t, 22, config.batched_shots_gpu); + read_value(t, 23, config.batched_shots_gpu_max_qubits); + read_value(t, 24, config.num_threads_per_device); + read_value(t, 25, config.statevector_parallel_threshold); + read_value(t, 26, config.statevector_sample_measure_opt); + read_value(t, 27, config.stabilizer_max_snapshot_probabilities); + read_value(t, 28, config.extended_stabilizer_sampling_method); + read_value(t, 29, config.extended_stabilizer_metropolis_mixing_time); + read_value(t, 20, config.extended_stabilizer_approximation_error); + read_value(t, 31, config.extended_stabilizer_norm_estimation_samples); + read_value(t, 32, config.extended_stabilizer_norm_estimation_repetitions); + read_value(t, 33, config.extended_stabilizer_parallel_threshold); + read_value(t, 34, config.extended_stabilizer_probabilities_snapshot_samples); + read_value(t, 35, config.matrix_product_state_truncation_threshold); + read_value(t, 36, config.matrix_product_state_max_bond_dimension); + read_value(t, 37, config.mps_sample_measure_algorithm); + read_value(t, 38, config.mps_log_data); + read_value(t, 39, config.mps_swap_direction); + read_value(t, 30, config.chop_threshold); + read_value(t, 41, config.mps_parallel_threshold); + read_value(t, 42, config.mps_omp_threads); + read_value(t, 43, config.tensor_network_num_sampling_qubits); + read_value(t, 44, config.use_cuTensorNet_autotuning); + read_value(t, 45, config.library_dir); + read_value(t, 46, config.param_table); + read_value(t, 47, config.n_qubits); + read_value(t, 48, config.global_phase); + read_value(t, 49, config.memory_slots); + read_value(t, 50, config._parallel_experiments); + read_value(t, 51, config._parallel_shots); + read_value(t, 52, config._parallel_state_update); + read_value(t, 53, config.fusion_allow_kraus); + read_value(t, 54, config.fusion_allow_superop); + read_value(t, 55, config.fusion_parallelization_threshold); + read_value(t, 56, config._fusion_enable_n_qubits); + read_value(t, 57, config._fusion_enable_n_qubits_1); + read_value(t, 58, config._fusion_enable_n_qubits_2); + read_value(t, 59, config._fusion_enable_n_qubits_3); + read_value(t, 60, config._fusion_enable_n_qubits_4); + read_value(t, 61, config._fusion_enable_n_qubits_5); + read_value(t, 62, config._fusion_enable_diagonal); + read_value(t, 63, config._fusion_min_qubit); + read_value(t, 64, config.fusion_cost_factor); + read_value(t, 65, config._fusion_enable_cost_based); + read_value(t, 66, config._fusion_cost_1); + read_value(t, 67, config._fusion_cost_2); + read_value(t, 68, config._fusion_cost_3); + read_value(t, 69, config._fusion_cost_4); + read_value(t, 70, config._fusion_cost_5); + read_value(t, 71, config._fusion_cost_6); + read_value(t, 72, config._fusion_cost_7); + read_value(t, 73, config._fusion_cost_8); + read_value(t, 74, config._fusion_cost_9); + read_value(t, 75, config._fusion_cost_10); + + read_value(t, 76, config.superoperator_parallel_threshold); + read_value(t, 77, config.unitary_parallel_threshold); + read_value(t, 78, config.memory_blocking_bits); + read_value(t, 79, config.extended_stabilizer_norm_estimation_default_samples); + return config; + })); +} +#endif diff --git a/qiskit_aer/backends/wrappers/aer_state_binding.hpp b/qiskit_aer/backends/wrappers/aer_state_binding.hpp new file mode 100644 index 0000000000..36fd4da9a4 --- /dev/null +++ b/qiskit_aer/backends/wrappers/aer_state_binding.hpp @@ -0,0 +1,183 @@ +/** + * This code is part of Qiskit. + * + * (C) Copyright IBM 2023. + * + * This code is licensed under the Apache License, Version 2.0. You may + * obtain a copy of this license in the LICENSE.txt file in the root directory + * of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. + * + * Any modifications or derivative works of this code must retain this + * copyright notice, and modified files need to carry a notice indicating + * that they have been altered from the originals. + */ + +#ifndef _aer_state_binding_hpp_ +#define _aer_state_binding_hpp_ + +#include "misc/warnings.hpp" +DISABLE_WARNING_PUSH +#include +DISABLE_WARNING_POP +#if defined(_MSC_VER) + #undef snprintf +#endif + +#include +#include + +#include "framework/matrix.hpp" +#include "framework/python_parser.hpp" +#include "framework/pybind_casts.hpp" +#include "framework/types.hpp" +#include "framework/results/pybind_result.hpp" + +#include "controllers/state_controller.hpp" + +namespace py = pybind11; +using namespace AER; + +template +void bind_aer_state(MODULE m) { + py::class_ aer_state(m, "AerStateWrapper"); + + aer_state.def(py::init<>(), "constructor"); + + aer_state.def("__repr__", [](const AerState &state) { + std::stringstream ss; + ss << "AerStateWrapper(" + << "initialized=" << state.is_initialized() + << ", num_of_qubits=" << state.num_of_qubits(); + ss << ")"; + return ss.str(); + }); + + aer_state.def("configure", &AerState::configure); + aer_state.def("allocate_qubits", &AerState::allocate_qubits); + aer_state.def("reallocate_qubits", &AerState::reallocate_qubits); + aer_state.def("set_random_seed", &AerState::set_random_seed); + aer_state.def("set_seed", &AerState::set_seed); + aer_state.def("clear", &AerState::clear); + aer_state.def("num_of_qubits", &AerState::num_of_qubits); + + aer_state.def("initialize", &AerState::initialize); + aer_state.def("initialize_statevector", [aer_state](AerState &state, + int num_of_qubits, + py::array_t> &values, + bool copy) { + auto c_contiguous = values.attr("flags").attr("c_contiguous").template cast(); + auto f_contiguous = values.attr("flags").attr("f_contiguous").template cast(); + if (!c_contiguous && !f_contiguous) + return false; + std::complex* data_ptr = reinterpret_cast*>(values.mutable_data(0)); + state.configure("method", "statevector"); + state.initialize_statevector(num_of_qubits, data_ptr, copy); + return true; + }); + + aer_state.def("initialize_density_matrix", [aer_state](AER::AerState &state, + int num_of_qubits, + py::array_t> &values, + bool copy) { + auto c_contiguous = values.attr("flags").attr("c_contiguous").template cast(); + auto f_contiguous = values.attr("flags").attr("f_contiguous").template cast(); + if (!c_contiguous && !f_contiguous) + return false; + std::complex* data_ptr = reinterpret_cast*>(values.mutable_data(0)); + state.configure("method", "density_matrix"); + state.initialize_density_matrix(num_of_qubits, data_ptr, f_contiguous, copy); + return true; + }); + + aer_state.def("move_to_buffer", [aer_state](AER::AerState &state) { + return state.move_to_vector().move_to_buffer(); + }); + + aer_state.def("move_to_ndarray", [aer_state](AerState &state) { + auto vec = state.move_to_vector(); + + auto ret = AerToPy::to_numpy(std::move(vec)); + return ret; + }); + + aer_state.def("move_to_matrix", [aer_state](AER::AerState &state) { + auto mat = state.move_to_matrix(); + auto ret = AerToPy::to_numpy(std::move(mat)); + return ret; + }); + + aer_state.def("flush", &AerState::flush_ops); + + aer_state.def("last_result", [aer_state](AerState &state) { + return AerToPy::to_python(state.last_result().to_json()); + }); + + + aer_state.def("apply_initialize", &AerState::apply_initialize); + aer_state.def("set_statevector", &AER::AerState::set_statevector); + aer_state.def("set_density_matrix", &AER::AerState::set_density_matrix); + + aer_state.def("apply_global_phase", &AerState::apply_global_phase); + aer_state.def("apply_unitary", [aer_state](AerState &state, + const reg_t &qubits, + const py::array_t> &values) { + size_t mat_len = (1UL << qubits.size()); + auto ptr = values.unchecked<2>(); + cmatrix_t mat(mat_len, mat_len); + for (auto i = 0; i < mat_len; ++i) + for (auto j = 0; j < mat_len; ++j) + mat(i, j) = ptr(i, j); + state.apply_unitary(qubits, mat); + }); + + aer_state.def("apply_multiplexer", [aer_state](AerState &state, + const reg_t &control_qubits, + const reg_t &target_qubits, + const py::array_t> &values) { + size_t mat_len = (1UL << target_qubits.size()); + size_t mat_size = (1UL << control_qubits.size()); + auto ptr = values.unchecked<3>(); + std::vector mats; + for (auto i = 0; i < mat_size; ++i) { + cmatrix_t mat(mat_len, mat_len); + for (auto j = 0; j < mat_len; ++j) + for (auto k = 0; k < mat_len; ++k) + mat(j, k) = ptr(i, j, k); + mats.push_back(mat); + } + state.apply_multiplexer(control_qubits, target_qubits, mats); + }); + + aer_state.def("apply_diagonal", &AerState::apply_diagonal_matrix); + aer_state.def("apply_x", &AerState::apply_x); + aer_state.def("apply_cx", &AerState::apply_cx); + aer_state.def("apply_mcx", &AerState::apply_mcx); + aer_state.def("apply_y", &AerState::apply_y); + aer_state.def("apply_cy", &AerState::apply_cy); + aer_state.def("apply_mcy", &AerState::apply_mcy); + aer_state.def("apply_z", &AerState::apply_z); + aer_state.def("apply_cz", &AerState::apply_cz); + aer_state.def("apply_mcz", &AerState::apply_mcz); + aer_state.def("apply_mcphase", &AerState::apply_mcphase); + aer_state.def("apply_h", &AerState::apply_h); + aer_state.def("apply_u", &AerState::apply_u); + aer_state.def("apply_cu", &AerState::apply_cu); + aer_state.def("apply_mcu", &AerState::apply_mcu); + aer_state.def("apply_mcswap", &AerState::apply_mcswap); + aer_state.def("apply_measure", &AerState::apply_measure); + aer_state.def("apply_reset", &AerState::apply_reset); + aer_state.def("apply_kraus", &AER::AerState::apply_kraus); + aer_state.def("probability", &AerState::probability); + aer_state.def("probabilities", [aer_state](AerState &state, + const reg_t qubits) { + if (qubits.empty()) + return state.probabilities(); + else + return state.probabilities(qubits); + }, py::arg("qubits") = reg_t()); + aer_state.def("sample_memory", &AerState::sample_memory); + aer_state.def("sample_counts", &AerState::sample_counts); + +} + +#endif \ No newline at end of file diff --git a/qiskit_aer/backends/wrappers/bindings.cc b/qiskit_aer/backends/wrappers/bindings.cc index aa3a78950d..89145c7045 100644 --- a/qiskit_aer/backends/wrappers/bindings.cc +++ b/qiskit_aer/backends/wrappers/bindings.cc @@ -12,29 +12,11 @@ DISABLE_WARNING_POP #undef snprintf #endif -#include "framework/matrix.hpp" -#include "framework/python_parser.hpp" -#include "framework/pybind_casts.hpp" -#include "framework/types.hpp" -#include "framework/results/pybind_result.hpp" +#include "aer_controller_binding.hpp" +#include "aer_state_binding.hpp" +#include "aer_circuit_binding.hpp" -#include "controllers/aer_controller.hpp" -#include "controllers/controller_execute.hpp" - -#include "controllers/state_controller.hpp" - -template -class ControllerExecutor { -public: - ControllerExecutor() = default; - py::object operator()(const py::handle &qobj) { -#ifdef TEST_JSON // Convert input qobj to json to test standalone data reading - return AerToPy::to_python(AER::controller_execute(json_t(qobj))); -#else - return AerToPy::to_python(AER::controller_execute(qobj)); -#endif - } -}; +using namespace AER; PYBIND11_MODULE(controller_wrappers, m) { @@ -42,149 +24,7 @@ PYBIND11_MODULE(controller_wrappers, m) { int prov; MPI_Init_thread(nullptr,nullptr,MPI_THREAD_MULTIPLE,&prov); #endif - - py::class_ > aer_ctrl (m, "aer_controller_execute"); - aer_ctrl.def(py::init<>()); - aer_ctrl.def("__call__", &ControllerExecutor::operator()); - aer_ctrl.def("__reduce__", [aer_ctrl](const ControllerExecutor &self) { - return py::make_tuple(aer_ctrl, py::tuple()); - }); - - py::class_ aer_state(m, "AerStateWrapper"); - - aer_state.def(py::init<>(), "constructor"); - - aer_state.def("__repr__", [](const AER::AerState &state) { - std::stringstream ss; - ss << "AerStateWrapper(" - << "initialized=" << state.is_initialized() - << ", num_of_qubits=" << state.num_of_qubits(); - ss << ")"; - return ss.str(); - }); - - aer_state.def("configure", &AER::AerState::configure); - aer_state.def("allocate_qubits", &AER::AerState::allocate_qubits); - aer_state.def("reallocate_qubits", &AER::AerState::reallocate_qubits); - aer_state.def("set_random_seed", &AER::AerState::set_random_seed); - aer_state.def("set_seed", &AER::AerState::set_seed); - aer_state.def("clear", &AER::AerState::clear); - aer_state.def("num_of_qubits", &AER::AerState::num_of_qubits); - - aer_state.def("initialize", &AER::AerState::initialize); - aer_state.def("initialize_statevector", [aer_state](AER::AerState &state, - int num_of_qubits, - py::array_t> &values, - bool copy) { - auto c_contiguous = values.attr("flags").attr("c_contiguous").template cast(); - auto f_contiguous = values.attr("flags").attr("f_contiguous").template cast(); - if (!c_contiguous && !f_contiguous) - return false; - std::complex* data_ptr = reinterpret_cast*>(values.mutable_data(0)); - state.configure("method", "statevector"); - state.initialize_statevector(num_of_qubits, data_ptr, copy); - return true; - }); - - aer_state.def("initialize_density_matrix", [aer_state](AER::AerState &state, - int num_of_qubits, - py::array_t> &values, - bool copy) { - auto c_contiguous = values.attr("flags").attr("c_contiguous").template cast(); - auto f_contiguous = values.attr("flags").attr("f_contiguous").template cast(); - if (!c_contiguous && !f_contiguous) - return false; - std::complex* data_ptr = reinterpret_cast*>(values.mutable_data(0)); - state.configure("method", "density_matrix"); - state.initialize_density_matrix(num_of_qubits, data_ptr, f_contiguous, copy); - return true; - }); - - aer_state.def("move_to_buffer", [aer_state](AER::AerState &state) { - return state.move_to_vector().move_to_buffer(); - }); - - aer_state.def("move_to_ndarray", [aer_state](AER::AerState &state) { - auto vec = state.move_to_vector(); - auto ret = AerToPy::to_numpy(std::move(vec)); - return ret; - }); - - aer_state.def("move_to_matrix", [aer_state](AER::AerState &state) { - auto mat = state.move_to_matrix(); - auto ret = AerToPy::to_numpy(std::move(mat)); - return ret; - }); - - aer_state.def("flush", &AER::AerState::flush_ops); - - aer_state.def("last_result", [aer_state](AER::AerState &state) { - return AerToPy::to_python(state.last_result().to_json()); - }); - - aer_state.def("apply_initialize", &AER::AerState::apply_initialize); - aer_state.def("set_statevector", &AER::AerState::set_statevector); - aer_state.def("set_density_matrix", &AER::AerState::set_density_matrix); - - aer_state.def("apply_global_phase", &AER::AerState::apply_global_phase); - aer_state.def("apply_unitary", [aer_state](AER::AerState &state, - const reg_t &qubits, - const py::array_t> &values) { - size_t mat_len = (1UL << qubits.size()); - auto ptr = values.unchecked<2>(); - AER::cmatrix_t mat(mat_len, mat_len); - for (auto i = 0; i < mat_len; ++i) - for (auto j = 0; j < mat_len; ++j) - mat(i, j) = ptr(i, j); - state.apply_unitary(qubits, mat); - }); - - aer_state.def("apply_multiplexer", [aer_state](AER::AerState &state, - const reg_t &control_qubits, - const reg_t &target_qubits, - const py::array_t> &values) { - size_t mat_len = (1UL << target_qubits.size()); - size_t mat_size = (1UL << control_qubits.size()); - auto ptr = values.unchecked<3>(); - std::vector mats; - for (auto i = 0; i < mat_size; ++i) { - AER::cmatrix_t mat(mat_len, mat_len); - for (auto j = 0; j < mat_len; ++j) - for (auto k = 0; k < mat_len; ++k) - mat(j, k) = ptr(i, j, k); - mats.push_back(mat); - } - state.apply_multiplexer(control_qubits, target_qubits, mats); - }); - - aer_state.def("apply_diagonal", &AER::AerState::apply_diagonal_matrix); - aer_state.def("apply_x", &AER::AerState::apply_x); - aer_state.def("apply_cx", &AER::AerState::apply_cx); - aer_state.def("apply_mcx", &AER::AerState::apply_mcx); - aer_state.def("apply_y", &AER::AerState::apply_y); - aer_state.def("apply_cy", &AER::AerState::apply_cy); - aer_state.def("apply_mcy", &AER::AerState::apply_mcy); - aer_state.def("apply_z", &AER::AerState::apply_z); - aer_state.def("apply_cz", &AER::AerState::apply_cz); - aer_state.def("apply_mcz", &AER::AerState::apply_mcz); - aer_state.def("apply_mcphase", &AER::AerState::apply_mcphase); - aer_state.def("apply_h", &AER::AerState::apply_h); - aer_state.def("apply_u", &AER::AerState::apply_u); - aer_state.def("apply_cu", &AER::AerState::apply_cu); - aer_state.def("apply_mcu", &AER::AerState::apply_mcu); - aer_state.def("apply_mcswap", &AER::AerState::apply_mcswap); - aer_state.def("apply_measure", &AER::AerState::apply_measure); - aer_state.def("apply_reset", &AER::AerState::apply_reset); - aer_state.def("apply_kraus", &AER::AerState::apply_kraus); - aer_state.def("probability", &AER::AerState::probability); - aer_state.def("probabilities", [aer_state](AER::AerState &state, - const reg_t qubits) { - if (qubits.empty()) - return state.probabilities(); - else - return state.probabilities(qubits); - }, py::arg("qubits") = reg_t()); - aer_state.def("sample_memory", &AER::AerState::sample_memory); - aer_state.def("sample_counts", &AER::AerState::sample_counts); - + bind_aer_controller(m); + bind_aer_state(m); + bind_aer_circuit(m); } diff --git a/qiskit_aer/jobs/aerjob.py b/qiskit_aer/jobs/aerjob.py index bfc3ca37e9..0bd0124264 100644 --- a/qiskit_aer/jobs/aerjob.py +++ b/qiskit_aer/jobs/aerjob.py @@ -15,6 +15,7 @@ """This module implements the job class used for AerBackend objects.""" import logging +import warnings from qiskit.providers import JobV1 as Job from qiskit.providers import JobStatus, JobError @@ -26,7 +27,10 @@ class AerJob(Job): """AerJob class for Qiskit Aer Simulators.""" - def __init__(self, backend, job_id, fn, qobj, executor=None): + def __init__(self, backend, job_id, fn, + qobj=None, + circuits=None, noise_model=None, config=None, + executor=None): """ Initializes the asynchronous job. Args: @@ -36,12 +40,32 @@ def __init__(self, backend, job_id, fn, qobj, executor=None): This should usually be a bound :meth:`AerBackend._run()` method, with the signature `(qobj: QasmQobj, job_id: str) -> Result`. qobj(QasmQobj): qobj to execute + circuits(list of QuantumCircuit): circuits to execute. + If `qobj` is set, this argument is ignored. + noise_model(NoiseModel): noise_model to execute. + If `qobj` is set, this argument is ignored. + config(dict): configuration to execute. + If `qobj` is set, this argument is ignored. executor(ThreadPoolExecutor or dask.distributed.client): The executor to be used to submit the job. + + Raises: + JobError: if no qobj and no circuits. """ super().__init__(backend, job_id) self._fn = fn - self._qobj = qobj + if qobj: + self._qobj = qobj + self._circuits = None + self._noise_model = None + self._config = None + elif circuits: + self._qobj = None + self._circuits = circuits + self._noise_model = noise_model + self._config = config + else: + raise JobError('AerJob needs a qobj or circuits') self._executor = executor or DEFAULT_EXECUTOR self._future = None @@ -55,7 +79,11 @@ def submit(self): """ if self._future is not None: raise JobError("Aer job has already been submitted.") - self._future = self._executor.submit(self._fn, self._qobj, self._job_id) + if self._qobj: + self._future = self._executor.submit(self._fn, self._qobj, self._job_id) + else: + self._future = self._executor.submit(self._fn, self._circuits, self._noise_model, + self._config, self._job_id) @requires_submit def result(self, timeout=None): @@ -118,8 +146,22 @@ def qobj(self): Returns: Qobj: the Qobj submitted for this job. """ + warnings.warn( + '`AerJob.qobj() is deprecated as of qiskit-aer 0.12.0`. ' + 'Using a qobj for `backend.run()` is deprecated as of qiskit-aer 0.9.0' + ' and will be removed no sooner than 3 months from that release' + ' date. Once it is removed, this `qobj()` returns always `None`.', + DeprecationWarning, stacklevel=2) return self._qobj + def circuits(self): + """Return the list of QuantumCircuit submitted for this job. + + Returns: + list of QuantumCircuit: the list of QuantumCircuit submitted for this job. + """ + return self._circuits + def executor(self): """Return the executor for this job""" return self._executor diff --git a/qiskit_aer/quantum_info/states/aer_state.py b/qiskit_aer/quantum_info/states/aer_state.py index efbf5b9b25..0f21602751 100644 --- a/qiskit_aer/quantum_info/states/aer_state.py +++ b/qiskit_aer/quantum_info/states/aer_state.py @@ -14,7 +14,8 @@ """ from enum import Enum import numpy as np -from qiskit.providers.aer.backends.controller_wrappers import AerStateWrapper +# pylint: disable=import-error, no-name-in-module +from qiskit_aer.backends.controller_wrappers import AerStateWrapper from ...backends.aerbackend import AerError diff --git a/releasenotes/notes/own_assembler-0c76e67a054bd12c.yaml b/releasenotes/notes/own_assembler-0c76e67a054bd12c.yaml new file mode 100644 index 0000000000..bcb53b5520 --- /dev/null +++ b/releasenotes/notes/own_assembler-0c76e67a054bd12c.yaml @@ -0,0 +1,24 @@ +--- +features: + - | + Aer simulates :class:`~qiskit.circuit.QuantumCircuit` without serialization to + :class:`~qiskit.qobj.QasmQobj`. Instead, Aer converts a circuit to + a :class:`.AerCircuit` instance that a wrapper of internal circuit structure + of Aer C++. With this change, because overheads of serialization is reduced, + performance of simulation especially for low-qubit circuts is improved. + Note that pulse simualation and DASK-based simulation still use Qobj and + :class`.AerJob` does not return Qobj otherwise. + - | + :class:`AerJob` has a new method :meth:`circuits` that return a list of + :class:`~qiskit.circuit.QuantumCircuit` This method returns `None` if Qobj + is used for simulation. +deprecations: + - | + A method :meth:`qobj` of :class:`AerJob` is deprecated because Using a qobj + is deprecated as of qiskit-aer 0.9.0. Once qobj support is removed, :meth:`qobj` + will return always `None`. +upgrade: + - | + If a circuit includes gates not in basis gates, previously Aer returns a result + with `ERROR` or `PARTIAL COMPLETED` status. From this release, Aer throws an + error when :meth:`reesult()` is called. diff --git a/src/controllers/aer_controller.hpp b/src/controllers/aer_controller.hpp index bc6ca12964..405e827392 100644 --- a/src/controllers/aer_controller.hpp +++ b/src/controllers/aer_controller.hpp @@ -46,6 +46,7 @@ #include "framework/results/experiment_result.hpp" #include "framework/results/result.hpp" #include "framework/rng.hpp" +#include "framework/config.hpp" #include "noise/noise_model.hpp" #include "transpile/cacheblocking.hpp" @@ -86,7 +87,7 @@ class Controller { Result execute(std::vector &circuits, Noise::NoiseModel &noise_model, - const json_t &config); + const Config &config); //----------------------------------------------------------------------- // Config settings @@ -94,7 +95,7 @@ class Controller { // Load Controller, State and Data config from a JSON // config settings will be passed to the State and Data classes - void set_config(const json_t &config); + void set_config(const Config &config); // Clear the current config void clear_config(); @@ -168,7 +169,7 @@ class Controller { // This method must initialize a state and return output data for // the required number of shots. void run_circuit(const Circuit &circ, const Noise::NoiseModel &noise, - const Method method,const json_t &config, ExperimentResult &result) const; + const Method method, const Config &config, ExperimentResult &result) const; //---------------------------------------------------------------- // Run circuit helpers @@ -177,7 +178,7 @@ class Controller { // Execute n-shots of a circuit on the input state template void run_circuit_helper(const Circuit &circ, const Noise::NoiseModel &noise, - const json_t &config, const Method method, + const Config &config, const Method method, ExperimentResult &result) const; // Execute a single shot a of circuit by initializing the state vector, @@ -204,14 +205,14 @@ class Controller { template void run_circuit_without_sampled_noise(Circuit &circ, const Noise::NoiseModel &noise, - const json_t &config, + const Config &config, const Method method, ExperimentResult &result) const; template void run_circuit_with_sampled_noise(const Circuit &circ, const Noise::NoiseModel &noise, - const json_t &config, + const Config &config, const Method method, ExperimentResult &result) const; @@ -280,14 +281,14 @@ class Controller { // method, circuit and config Transpile::Fusion transpile_fusion(Method method, const Operations::OpSet &opset, - const json_t &config) const; + const Config &config) const; // Return cache blocking transpiler pass Transpile::CacheBlocking transpile_cache_blocking(Controller::Method method, const Circuit &circ, const Noise::NoiseModel &noise, - const json_t &config) const; + const Config &config) const; //return maximum number of qubits for matrix int_t get_max_matrix_qubits(const Circuit &circ) const; @@ -390,23 +391,23 @@ class Controller { // Config settings //------------------------------------------------------------------------- -void Controller::set_config(const json_t &config) { +void Controller::set_config(const Config &config) { // Load validation threshold - JSON::get_value(validation_threshold_, "validation_threshold", config); + validation_threshold_ = config.validation_threshold; // Load config for memory (creg list data) - JSON::get_value(save_creg_memory_, "memory", config); + if (config.memory.has_value()) + save_creg_memory_ = config.memory.value(); #ifdef _OPENMP // Load OpenMP maximum thread settings - if (JSON::check_key("max_parallel_threads", config)) - JSON::get_value(max_parallel_threads_, "max_parallel_threads", config); - if (JSON::check_key("max_parallel_experiments", config)) - JSON::get_value(max_parallel_experiments_, "max_parallel_experiments", - config); - if (JSON::check_key("max_parallel_shots", config)) - JSON::get_value(max_parallel_shots_, "max_parallel_shots", config); + if (config.max_parallel_threads.has_value()) + max_parallel_threads_ = config.max_parallel_threads.value(); + if (config.max_parallel_experiments.has_value()) + max_parallel_experiments_ = config.max_parallel_experiments.value(); + if (config.max_parallel_shots.has_value()) + max_parallel_shots_ = config.max_parallel_shots.value(); // Limit max threads based on number of available OpenMP threads auto omp_threads = omp_get_max_threads(); max_parallel_threads_ = (max_parallel_threads_ > 0) @@ -422,25 +423,24 @@ void Controller::set_config(const json_t &config) { // Load configurations for parallelization - if (JSON::check_key("max_memory_mb", config)) { - JSON::get_value(max_memory_mb_, "max_memory_mb", config); - } + if (config.max_memory_mb.has_value()) + max_memory_mb_ = config.max_memory_mb.value(); // for debugging - if (JSON::check_key("_parallel_experiments", config)) { - JSON::get_value(parallel_experiments_, "_parallel_experiments", config); + if (config._parallel_experiments.has_value()) { + parallel_experiments_ = config._parallel_experiments.value(); explicit_parallelization_ = true; } // for debugging - if (JSON::check_key("_parallel_shots", config)) { - JSON::get_value(parallel_shots_, "_parallel_shots", config); + if (config._parallel_shots.has_value()) { + parallel_shots_ = config._parallel_shots.value(); explicit_parallelization_ = true; } // for debugging - if (JSON::check_key("_parallel_state_update", config)) { - JSON::get_value(parallel_state_update_, "_parallel_state_update", config); + if (config._parallel_state_update.has_value()) { + parallel_state_update_ = config._parallel_state_update.value(); explicit_parallelization_ = true; } @@ -450,95 +450,83 @@ void Controller::set_config(const json_t &config) { parallel_state_update_ = std::max({parallel_state_update_, 1}); } - if (JSON::check_key("accept_distributed_results", config)) { - JSON::get_value(accept_distributed_results_, "accept_distributed_results", - config); - } + if (config.accept_distributed_results.has_value()) + accept_distributed_results_ = config.accept_distributed_results.value(); // enable multiple qregs if cache blocking is enabled - cache_block_qubit_ = 0; - if (JSON::check_key("blocking_qubits", config)) { - JSON::get_value(cache_block_qubit_, "blocking_qubits", config); - } + if (config.blocking_qubits.has_value()) + cache_block_qubit_ = config.blocking_qubits.value(); //enable batched multi-shots/experiments optimization - if(JSON::check_key("batched_shots_gpu", config)) { - JSON::get_value(batched_shots_gpu_, "batched_shots_gpu", config); - } - if(JSON::check_key("batched_shots_gpu_max_qubits", config)) { - JSON::get_value(batched_shots_gpu_max_qubits_, "batched_shots_gpu_max_qubits", config); - } + batched_shots_gpu_ = config.batched_shots_gpu; + batched_shots_gpu_max_qubits_ = config.batched_shots_gpu_max_qubits; //cuStateVec configs cuStateVec_enable_ = false; - if(JSON::check_key("cuStateVec_enable", config)) { - JSON::get_value(cuStateVec_enable_, "cuStateVec_enable", config); - } + if (config.cuStateVec_enable.has_value()) + cuStateVec_enable_ = config.cuStateVec_enable.value(); // Override automatic simulation method with a fixed method - std::string method; - if (JSON::get_value(method, "method", config)) { - if (method == "statevector") { - method_ = Method::statevector; - } else if (method == "density_matrix") { - method_ = Method::density_matrix; - } else if (method == "stabilizer") { - method_ = Method::stabilizer; - } else if (method == "extended_stabilizer") { - method_ = Method::extended_stabilizer; - } else if (method == "matrix_product_state") { - method_ = Method::matrix_product_state; - } else if (method == "unitary") { - method_ = Method::unitary; - } else if (method == "superop") { - method_ = Method::superop; - } else if (method == "tensor_network") { - method_ = Method::tensor_network; - } else if (method != "automatic") { - throw std::runtime_error(std::string("Invalid simulation method (") + - method + std::string(").")); - } + std::string method = config.method; + if (config.method == "statevector") { + method_ = Method::statevector; + } else if (config.method == "density_matrix") { + method_ = Method::density_matrix; + } else if (config.method == "stabilizer") { + method_ = Method::stabilizer; + } else if (config.method == "extended_stabilizer") { + method_ = Method::extended_stabilizer; + } else if (config.method == "matrix_product_state") { + method_ = Method::matrix_product_state; + } else if (config.method == "unitary") { + method_ = Method::unitary; + } else if (config.method == "superop") { + method_ = Method::superop; + } else if (config.method == "tensor_network") { + method_ = Method::tensor_network; + } else if (config.method != "automatic") { + throw std::runtime_error(std::string("Invalid simulation method (") + + method + std::string(").")); } if(method_ == Method::density_matrix || method_ == Method::unitary) batched_shots_gpu_max_qubits_ /= 2; // Override automatic simulation method with a fixed method - if (JSON::get_value(sim_device_name_, "device", config)) { - if (sim_device_name_ == "CPU") { - sim_device_ = Device::CPU; - } else if (sim_device_name_ == "Thrust") { + sim_device_name_ = config.device; + if (sim_device_name_ == "CPU") { + sim_device_ = Device::CPU; + } else if (sim_device_name_ == "Thrust") { #ifndef AER_THRUST_CPU - throw std::runtime_error( - "Simulation device \"Thrust\" is not supported on this system"); + throw std::runtime_error( + "Simulation device \"Thrust\" is not supported on this system"); #else - sim_device_ = Device::ThrustCPU; + sim_device_ = Device::ThrustCPU; #endif - } else if (sim_device_name_ == "GPU") { + } else if (sim_device_name_ == "GPU") { #ifndef AER_THRUST_CUDA - throw std::runtime_error( - "Simulation device \"GPU\" is not supported on this system"); + throw std::runtime_error( + "Simulation device \"GPU\" is not supported on this system"); #else #ifndef AER_CUSTATEVEC - if(cuStateVec_enable_){ - //Aer is not built for cuStateVec - throw std::runtime_error( - "Simulation device \"GPU\" does not support cuStateVec on this system"); - } -#endif - int nDev; - if (cudaGetDeviceCount(&nDev) != cudaSuccess) { - cudaGetLastError(); - throw std::runtime_error("No CUDA device available!"); - } - sim_device_ = Device::GPU; -#endif + if(cuStateVec_enable_){ + //Aer is not built for cuStateVec + throw std::runtime_error( + "Simulation device \"GPU\" does not support cuStateVec on this system"); } - else { - throw std::runtime_error(std::string("Invalid simulation device (\"") + - sim_device_name_ + std::string("\").")); +#endif + int nDev; + if (cudaGetDeviceCount(&nDev) != cudaSuccess) { + cudaGetLastError(); + throw std::runtime_error("No CUDA device available!"); } + sim_device_ = Device::GPU; +#endif + } + else { + throw std::runtime_error(std::string("Invalid simulation device (\"") + + sim_device_name_ + std::string("\").")); } if(method_ == Method::tensor_network){ @@ -548,16 +536,14 @@ void Controller::set_config(const json_t &config) { throw std::runtime_error("Invalid combination of simulation method and device, \"tensor_network\" only supports \"device=GPU\""); } - std::string precision; - if (JSON::get_value(precision, "precision", config)) { - if (precision == "double") { - sim_precision_ = Precision::Double; - } else if (precision == "single") { - sim_precision_ = Precision::Single; - } else { - throw std::runtime_error(std::string("Invalid simulation precision (") + - precision + std::string(").")); - } + std::string precision = config.precision; + if (precision == "double") { + sim_precision_ = Precision::Double; + } else if (precision == "single") { + sim_precision_ = Precision::Single; + } else { + throw std::runtime_error(std::string("Invalid simulation precision (") + + precision + std::string(").")); } } @@ -839,7 +825,7 @@ size_t Controller::get_gpu_memory_mb() { Transpile::CacheBlocking Controller::transpile_cache_blocking(Controller::Method method, const Circuit &circ, const Noise::NoiseModel &noise, - const json_t &config) const + const Config &config) const { Transpile::CacheBlocking cache_block_pass; @@ -922,7 +908,7 @@ Result Controller::execute(const inputdata_t &input_qobj) { Result Controller::execute(std::vector &circuits, Noise::NoiseModel &noise_model, - const json_t &config) + const Config &config) { // Start QOBJ timer auto timer_start = myclock_t::now(); @@ -1057,7 +1043,7 @@ Result Controller::execute(std::vector &circuits, // Base class override //------------------------------------------------------------------------- void Controller::run_circuit(const Circuit &circ, const Noise::NoiseModel &noise, - const Method method,const json_t &config, ExperimentResult &result) const + const Method method,const Config &config, ExperimentResult &result) const { // Run the circuit switch (method) { @@ -1261,7 +1247,7 @@ size_t Controller::required_memory_mb(const Circuit &circ, Transpile::Fusion Controller::transpile_fusion(Method method, const Operations::OpSet &opset, - const json_t &config) const { + const Config &config) const { Transpile::Fusion fusion_pass; fusion_pass.set_parallelization(parallel_state_update_); @@ -1326,7 +1312,7 @@ Transpile::Fusion Controller::transpile_fusion(Method method, template void Controller::run_circuit_helper(const Circuit &circ, const Noise::NoiseModel &noise, - const json_t &config, + const Config &config, const Method method, ExperimentResult &result) const { @@ -1470,7 +1456,7 @@ void Controller::run_with_sampling(const Circuit &circ, template void Controller::run_circuit_without_sampled_noise(Circuit &circ, const Noise::NoiseModel &noise, - const json_t &config, + const Config &config, const Method method, ExperimentResult &result) const { @@ -1628,7 +1614,7 @@ void Controller::run_circuit_without_sampled_noise(Circuit &circ, template void Controller::run_circuit_with_sampled_noise( - const Circuit &circ, const Noise::NoiseModel &noise, const json_t &config, + const Circuit &circ, const Noise::NoiseModel &noise, const Config &config, const Method method, ExperimentResult &result) const { std::vector par_results(parallel_shots_); diff --git a/src/controllers/controller_execute.hpp b/src/controllers/controller_execute.hpp index acb4d59d28..706f2d907d 100755 --- a/src/controllers/controller_execute.hpp +++ b/src/controllers/controller_execute.hpp @@ -17,7 +17,11 @@ #include #include "misc/hacks.hpp" + #include "framework/results/result.hpp" +#include "framework/python_parser.hpp" +#include "framework/matrix.hpp" +#include "framework/config.hpp" //========================================================================= // Controller Execute interface @@ -40,6 +44,114 @@ Result controller_execute(const inputdata_t& qobj) { return controller.execute(qobj); } +template +Result controller_execute(std::vector& input_circs, AER::Noise::NoiseModel &noise_model, AER::Config &config) { + controller_t controller; + + bool truncate = config.enable_truncation; + + if (noise_model.has_nonlocal_quantum_errors()) + truncate = false; + + const size_t num_circs = input_circs.size(); + + // Check if parameterized circuits + // It should be of the form + // [exp0_params, exp1_params, ...] + // where: + // expk_params = [((i, j), pars), ....] + // i is the instruction index in the experiment + // j is the param index in the instruction + // pars = [par0, par1, ...] is a list of different parameterizations + using pos_t = std::pair; + using exp_params_t = std::vector>>; + std::vector param_table = config.param_table; + + // Validate parameterizations for number of circuis + if (!param_table.empty() && param_table.size() != num_circs) { + throw std::invalid_argument( + R"(Invalid parameterized circuits: "parameterizations" length does not match number of circuits.)"); + } + + std::vector circs; + + try { + // Load circuits + for (size_t i=0; i= num_instr) { + throw std::invalid_argument(R"(Invalid parameterized qobj: instruction position out of range)"); + } + auto &op = param_circ.ops[instr_pos]; + if (param_pos >= op.params.size()) { + throw std::invalid_argument(R"(Invalid parameterized qobj: instruction param position out of range)"); + } + if (j >= params.second.size()) { + throw std::invalid_argument(R"(Invalid parameterized qobj: parameterization value out of range)"); + } + // Update the param + op.params[param_pos] = params.second[j]; + } + // Run truncation. + // TODO: Truncation should be performed and parameters should be resolved after it. + // However, parameters are associated with indices of instructions, which can be changed in truncation. + // Therefore, current implementation performs truncation for each parameter set. + if (truncate) { + param_circ.set_params(true); + param_circ.set_metadata(config, true); + } + circs.push_back(std::move(param_circ)); + } + } + } + } catch (std::exception &e) { + Result result; + + result.status = Result::Status::error; + result.message = std::string("Failed to load circuits: ") + e.what(); + return result; + } + + int_t seed = -1; + uint_t seed_shift = 0; + + if (config.seed_simulator.has_value()) + seed = config.seed_simulator.value(); + else + seed = circs[0].seed; + + for (auto& circ: circs) { + circ.seed = seed + seed_shift; + seed_shift += 2113; + } + + // Fix for MacOS and OpenMP library double initialization crash. + // Issue: https://github.com/Qiskit/qiskit-aer/issues/1 + Hacks::maybe_load_openmp(config.library_dir); + controller.set_config(config); + return controller.execute(circs, noise_model, config); +} + } // end namespace AER #endif diff --git a/src/framework/circuit.hpp b/src/framework/circuit.hpp index 9fae5e774f..b5471e4e43 100755 --- a/src/framework/circuit.hpp +++ b/src/framework/circuit.hpp @@ -19,6 +19,9 @@ #include "framework/operations.hpp" #include "framework/opset.hpp" +#include "framework/config.hpp" + +using complex_t = std::complex; namespace AER { @@ -100,9 +103,151 @@ class Circuit { // to end of circuit void set_params(bool truncation = false); + // Set metadata from a specified configuration. + // If `truncation = false`, some configurations related to qubits will be + // overriden because `set_params(false)` sets minimum qubits to simulate + // circuits. + void set_metadata(const AER::Config &config, bool truncation); + // Set the circuit rng seed to random value inline void set_random_seed() {seed = std::random_device()();} + //----------------------------------------------------------------------- + // Op insert helpers + //----------------------------------------------------------------------- + + void bfunc(const std::string &mask, const std::string &val, const std::string &relation, const uint_t regidx) { + ops.push_back(Operations::make_bfunc(mask, val, relation, regidx)); + } + + void gate(const std::string &name, + const reg_t &qubits, + const std::vector ¶ms, + const std::vector &string_params, + const int_t cond_regidx=-1, + const std::string label="") { + ops.push_back(Operations::make_gate(name, qubits, params, string_params, cond_regidx, label)); + } + + void diagonal(const reg_t &qubits, + const cvector_t &vec, + const std::string &label) { + ops.push_back(Operations::make_diagonal(qubits, vec, label)); + } + + void unitary(const reg_t &qubits, + const cmatrix_t &mat, + const int_t cond_regidx=-1, + const std::string label="") { + ops.push_back(Operations::make_unitary(qubits, mat, cond_regidx, label)); + } + + void initialize(const reg_t &qubits, + const std::vector &init_data) { + ops.push_back(Operations::make_initialize(qubits, init_data)); + } + + void roerror(const reg_t &memory, + const std::vector &probabilities) { + ops.push_back(Operations::make_roerror(memory, probabilities)); + } + + void multiplexer(const reg_t &qubits, + const std::vector &mats, + const int_t cond_regidx = -1, + std::string label="") { + ops.push_back(Operations::make_multiplexer(qubits, mats, cond_regidx, label)); + } + + void kraus(const reg_t &qubits, + const std::vector &mats, + const int_t cond_regidx = -1) { + ops.push_back(Operations::make_kraus(qubits, mats, cond_regidx)); + } + + void superop(const reg_t &qubits, + const cmatrix_t &mat, + const int_t cond_regidx = -1) { + ops.push_back(Operations::make_superop(qubits, mat, cond_regidx)); + } + + void save_state(const reg_t &qubits, + const std::string &name, + const std::string &snapshot_type, + const std::string &label="") { + ops.push_back(Operations::make_save_state(qubits, name, snapshot_type, label)); + } + + void save_amplitudes(const reg_t &qubits, + const std::string &name, + const std::vector &basis_state, + const std::string &snapshot_type, + const std::string &label="") { + ops.push_back(Operations::make_save_amplitudes(qubits, name, basis_state, snapshot_type, label)); + } + + void save_expval(const reg_t &qubits, + const std::string &name, + const std::vector pauli_strings, + const std::vector coeff_reals, + const std::vector coeff_imags, + const std::string &snapshot_type, + const std::string label="") { + ops.push_back(Operations::make_save_expval(qubits, name, pauli_strings, coeff_reals, coeff_imags, snapshot_type, label)); + } + + void set_qerror_loc(const reg_t &qubits, + const std::string &label, + const int_t conditional = -1) { + ops.push_back(Operations::make_qerror_loc(qubits, label, conditional)); + } + + template + void set_statevector(const reg_t &qubits, const inputdata_t ¶m) { + ops.push_back(Operations::make_set_vector(qubits, "set_statevector", param)); + } + + template + void set_density_matrix(const reg_t &qubits, const inputdata_t ¶m) { + ops.push_back(Operations::make_set_matrix(qubits, "set_density_matrix", param)); + } + + template + void set_unitary(const reg_t &qubits, const inputdata_t ¶m) { + ops.push_back(Operations::make_set_matrix(qubits, "set_unitary", param)); + } + + template + void set_superop(const reg_t &qubits, const inputdata_t ¶m) { + ops.push_back(Operations::make_set_matrix(qubits, "set_superop", param)); + } + + template + void set_matrix_product_state(const reg_t &qubits, const inputdata_t ¶m) { + ops.push_back(Operations::make_set_mps(qubits, "set_matrix_product_state", param)); + } + + template + void set_clifford(const reg_t &qubits, const inputdata_t ¶m) { + ops.push_back(Operations::make_set_clifford(qubits, "set_clifford", param)); + } + + void jump(const reg_t &qubits, const std::vector ¶ms, const int_t cond_regidx = -1) { + ops.push_back(Operations::make_jump(qubits, params, cond_regidx)); + } + + void mark(const reg_t &qubits, const std::vector ¶ms) { + ops.push_back(Operations::make_mark(qubits, params)); + } + + void measure(const reg_t &qubits, const reg_t &memory, const reg_t ®isters) { + ops.push_back(Operations::make_measure(qubits, memory, registers)); + } + + void reset(const reg_t &qubits) { + ops.push_back(Operations::make_reset(qubits)); + } + private: Operations::OpSet opset_; // Set of operation types contained in circuit std::set qubitset_; // Set of qubits used in the circuit @@ -152,7 +297,7 @@ Circuit::Circuit(const inputdata_t &circ, bool truncation) : Circuit(circ, json_ template Circuit::Circuit(const inputdata_t &circ, const json_t &qobj_config, bool truncation) : Circuit() { // Get config - json_t config = qobj_config; + auto config = qobj_config; if (Parser::check_key("config", circ)) { json_t circ_config; Parser::get_value(circ_config, "config", circ); @@ -161,11 +306,10 @@ Circuit::Circuit(const inputdata_t &circ, const json_t &qobj_config, bool trunca } } - // Load metadata + // Set header Parser::get_value(header, "header", circ); - Parser::get_value(shots, "shots", config); Parser::get_value(global_phase_angle, "global_phase", header); - + // Load instructions if (Parser::check_key("instructions", circ) == false) { throw std::invalid_argument("Invalid Qobj experiment: no \"instructions\" field."); @@ -181,11 +325,17 @@ Circuit::Circuit(const inputdata_t &circ, const json_t &qobj_config, bool trunca converted_ops.emplace_back(Operations::input_to_op(the_op)); } ops = std::move(converted_ops); + set_params(truncation); + set_metadata(config, truncation); +} + +void Circuit::set_metadata(const AER::Config &config, bool truncation) { + // Load metadata + shots = config.shots; // Check for specified memory slots - uint_t memory_slots = 0; - Parser::get_value(memory_slots, "memory_slots", config); + uint_t memory_slots = config.memory_slots; if (memory_slots < num_memory) { throw std::invalid_argument("Invalid Qobj experiment: not enough memory slots."); } @@ -193,10 +343,8 @@ Circuit::Circuit(const inputdata_t &circ, const json_t &qobj_config, bool trunca num_memory = memory_slots; // Check for specified n_qubits - if (Parser::check_key("n_qubits", config)) { - // uint_t n_qubits = config["n_qubits"]; - uint_t n_qubits; - Parser::get_value(n_qubits, "n_qubits", config); + if (config.n_qubits.has_value()) { + uint_t n_qubits = config.n_qubits.value(); if (n_qubits < num_qubits) { throw std::invalid_argument("Invalid Qobj experiment: n_qubits < instruction qubits."); } diff --git a/src/framework/config.hpp b/src/framework/config.hpp new file mode 100644 index 0000000000..59b0c76004 --- /dev/null +++ b/src/framework/config.hpp @@ -0,0 +1,447 @@ +/** + * This code is part of Qiskit. + * + * (C) Copyright IBM 2018, 2019. + * + * This code is licensed under the Apache License, Version 2.0. You may + * obtain a copy of this license in the LICENSE.txt file in the root directory + * of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. + * + * Any modifications or derivative works of this code must retain this + * copyright notice, and modified files need to carry a notice indicating + * that they have been altered from the originals. + */ + +#ifndef _aer_framework_config_hpp_ +#define _aer_framework_config_hpp_ + +#include +#include +#include "types.hpp" + +namespace AER { + +// very simple mimic of std::optional of C++-17 +template +struct optional { + T val; + bool exist = false; + + T value() const { + if (!exist) + throw std::runtime_error("value does not exist."); + return val; + } + + void value(const T& input) { + exist = true; + val = input; + } + + void clear() { + exist = false; + } + + // operator bool() const { + // return exist; + // } + + bool has_value() const { + return exist; + } +}; + +template +bool get_value(optional &var, const std::string& key, const json_t &js) { + if (JSON::check_key(key, js)) { + var.value(js[key].get()); + return true; + } else { + var.exist = false; + return false; + } +} + +template +bool get_value(T &var, const std::string& key, const json_t &js) { + return JSON::get_value(var, key, js); +} + +// Configuration of Aer simulation +// This class is corresponding to `AerSimulator._default_options()`. +struct Config { + // # Global options + uint_t shots = 1024; + std::string method = "automatic"; + std::string device = "CPU"; + std::string precision ="double"; + // executor=None, + // max_job_size=None, + // max_shot_size=None, + bool enable_truncation = true; + double zero_threshold = 1e-10; + double validation_threshold = 1e-8; + optional max_parallel_threads; + optional max_parallel_experiments; + optional max_parallel_shots; + optional max_memory_mb; + bool fusion_enable=true; + bool fusion_verbose=false; + optional fusion_max_qubit; + optional fusion_threshold; + optional accept_distributed_results; + optional memory; + // noise_model=None, + optional seed_simulator; + // # cuStateVec (cuQuantum) option + optional cuStateVec_enable; + // # cache blocking for multi-GPUs/MPI options + optional blocking_qubits; + bool blocking_enable = false; + optional chunk_swap_buffer_qubits; + // # multi-shots optimization options (GPU only) + bool batched_shots_gpu = false; + uint_t batched_shots_gpu_max_qubits = 16; + optional num_threads_per_device; + // # statevector options + uint_t statevector_parallel_threshold = 14; + uint_t statevector_sample_measure_opt = 10; + // # stabilizer options + uint_t stabilizer_max_snapshot_probabilities = 32; + // # extended stabilizer options + std::string extended_stabilizer_sampling_method = "resampled_metropolis"; + uint_t extended_stabilizer_metropolis_mixing_time = 5000; + double extended_stabilizer_approximation_error = 0.05; + uint_t extended_stabilizer_norm_estimation_samples = 100; + uint_t extended_stabilizer_norm_estimation_repetitions = 3; + uint_t extended_stabilizer_parallel_threshold = 100; + uint_t extended_stabilizer_probabilities_snapshot_samples = 3000; + // # MPS options + double matrix_product_state_truncation_threshold = 1e-16; + optional matrix_product_state_max_bond_dimension; + std::string mps_sample_measure_algorithm = "mps_heuristic"; + bool mps_log_data = false; + std::string mps_swap_direction = "mps_swap_left"; + double chop_threshold = 1e-8; + uint_t mps_parallel_threshold = 14; + uint_t mps_omp_threads = 1; + // # tensor network options + uint_t tensor_network_num_sampling_qubits = 10; + bool use_cuTensorNet_autotuning = false; + + // system configurations + std::string library_dir = ""; + using pos_t = std::pair; + using exp_params_t = std::vector>>; + std::vector param_table; + optional n_qubits; + double global_phase = 0.0; + uint_t memory_slots = 0; + optional _parallel_experiments; + optional _parallel_shots; + optional _parallel_state_update; + optional fusion_allow_kraus; + optional fusion_allow_superop; + optional fusion_parallelization_threshold; + optional _fusion_enable_n_qubits; + optional _fusion_enable_n_qubits_1; + optional _fusion_enable_n_qubits_2; + optional _fusion_enable_n_qubits_3; + optional _fusion_enable_n_qubits_4; + optional _fusion_enable_n_qubits_5; + optional _fusion_enable_diagonal; + optional _fusion_min_qubit; + optional fusion_cost_factor; + optional _fusion_enable_cost_based; + optional _fusion_cost_1; + optional _fusion_cost_2; + optional _fusion_cost_3; + optional _fusion_cost_4; + optional _fusion_cost_5; + optional _fusion_cost_6; + optional _fusion_cost_7; + optional _fusion_cost_8; + optional _fusion_cost_9; + optional _fusion_cost_10; + + optional superoperator_parallel_threshold; + optional unitary_parallel_threshold; + optional memory_blocking_bits; + optional extended_stabilizer_norm_estimation_default_samples; + + void clear() { + shots = 1024; + method = "automatic"; + device = "CPU"; + precision ="double"; + // executor=None, + // max_job_size=None, + // max_shot_size=None, + enable_truncation = true; + zero_threshold = 1e-10; + validation_threshold = 1e-8; + max_parallel_threads.clear(); + max_parallel_experiments.clear(); + max_parallel_shots.clear(); + max_memory_mb.clear(); + fusion_enable=true; + fusion_verbose=false; + fusion_max_qubit.clear(); + fusion_threshold.clear(); + accept_distributed_results.clear(); + memory.clear(); + // noise_model=None, + seed_simulator.clear(); + // # cuStateVec (cuQuantum) option + cuStateVec_enable.clear(); + // # cache blocking for multi-GPUs/MPI options + blocking_qubits.clear(); + blocking_enable = false; + chunk_swap_buffer_qubits.clear(); + // # multi-shots optimization options (GPU only) + batched_shots_gpu = false; + batched_shots_gpu_max_qubits = 16; + num_threads_per_device.clear(); + // # statevector options + statevector_parallel_threshold = 14; + statevector_sample_measure_opt = 10; + // # stabilizer options + stabilizer_max_snapshot_probabilities = 32; + // # extended stabilizer options + extended_stabilizer_sampling_method = "resampled_metropolis"; + extended_stabilizer_metropolis_mixing_time = 5000; + extended_stabilizer_approximation_error = 0.05; + extended_stabilizer_norm_estimation_samples = 100; + extended_stabilizer_norm_estimation_repetitions = 3; + extended_stabilizer_parallel_threshold = 100; + extended_stabilizer_probabilities_snapshot_samples = 3000; + // # MPS options + matrix_product_state_truncation_threshold = 1e-16; + matrix_product_state_max_bond_dimension.clear(); + mps_sample_measure_algorithm = "mps_heuristic"; + mps_log_data = false; + mps_swap_direction = "mps_swap_left"; + chop_threshold = 1e-8; + mps_parallel_threshold = 14; + mps_omp_threads = 1; + // # tensor network options + tensor_network_num_sampling_qubits = 10; + use_cuTensorNet_autotuning = false; + + // system configurations + param_table.clear(); + library_dir = ""; + n_qubits.clear(); + global_phase = 0.0; + memory_slots = 0; + _parallel_experiments.clear(); + _parallel_shots.clear(); + _parallel_state_update.clear(); + fusion_allow_kraus.clear(); + fusion_allow_superop.clear(); + fusion_parallelization_threshold.clear(); + _fusion_enable_n_qubits.clear(); + _fusion_enable_n_qubits_1.clear(); + _fusion_enable_n_qubits_2.clear(); + _fusion_enable_n_qubits_3.clear(); + _fusion_enable_n_qubits_4.clear(); + _fusion_enable_n_qubits_5.clear(); + _fusion_min_qubit.clear(); + fusion_cost_factor.clear(); + _fusion_enable_cost_based.clear(); + _fusion_cost_1.clear(); + _fusion_cost_2.clear(); + _fusion_cost_3.clear(); + _fusion_cost_4.clear(); + _fusion_cost_5.clear(); + _fusion_cost_6.clear(); + _fusion_cost_7.clear(); + _fusion_cost_8.clear(); + _fusion_cost_9.clear(); + _fusion_cost_10.clear(); + + superoperator_parallel_threshold.clear(); + unitary_parallel_threshold.clear(); + memory_blocking_bits.clear(); + extended_stabilizer_norm_estimation_default_samples.clear(); + } + + void merge(const Config& other) { + shots = other.shots; + method = other.method; + device = other.device; + precision = other.precision; + // executor=None, + // max_job_size=None, + // max_shot_size=None, + enable_truncation = other.enable_truncation; + zero_threshold = other.zero_threshold; + validation_threshold = other.validation_threshold; + if (other.max_parallel_threads.has_value()) max_parallel_threads.value(other.max_parallel_threads.value()); + if (other.max_parallel_experiments.has_value()) max_parallel_experiments.value(other.max_parallel_experiments.value()); + if (other.max_parallel_shots.has_value()) max_parallel_shots.value(other.max_parallel_shots.value()); + if (other.max_memory_mb.has_value()) max_memory_mb.value(other.max_memory_mb.value()); + fusion_enable = other.fusion_enable; + fusion_verbose = other.fusion_verbose; + if (other.fusion_max_qubit.has_value()) fusion_max_qubit.value(other.fusion_max_qubit.value()); + if (other.fusion_threshold.has_value()) fusion_threshold.value(other.fusion_threshold.value()); + if (other.accept_distributed_results.has_value()) accept_distributed_results.value(other.accept_distributed_results.value()); + if (other.memory.has_value()) memory.value(other.memory.value()); + // noise_model=None, + if (other.seed_simulator.has_value()) seed_simulator.value(other.seed_simulator.value()); + // # cuStateVec (cuQuantum) option + if (other.cuStateVec_enable.has_value()) cuStateVec_enable.value(other.cuStateVec_enable.value()); + // # cache blocking for multi-GPUs/MPI options + if (other.blocking_qubits.has_value()) blocking_qubits.value(other.blocking_qubits.value()); + blocking_enable = other.blocking_enable; + if (other.chunk_swap_buffer_qubits.has_value()) chunk_swap_buffer_qubits.value(other.chunk_swap_buffer_qubits.value()); + // # multi-shots optimization options (GPU only) + batched_shots_gpu = other.batched_shots_gpu; + batched_shots_gpu_max_qubits = other.batched_shots_gpu_max_qubits; + if (other.num_threads_per_device.has_value()) num_threads_per_device.value(other.num_threads_per_device.value()); + // # statevector options + statevector_parallel_threshold = other.statevector_parallel_threshold; + statevector_sample_measure_opt = other.statevector_sample_measure_opt; + // # stabilizer options + stabilizer_max_snapshot_probabilities = other.stabilizer_max_snapshot_probabilities; + // # extended stabilizer options + extended_stabilizer_sampling_method = other.extended_stabilizer_sampling_method; + extended_stabilizer_metropolis_mixing_time = other.extended_stabilizer_metropolis_mixing_time; + extended_stabilizer_approximation_error = other.extended_stabilizer_approximation_error; + extended_stabilizer_norm_estimation_samples = other.extended_stabilizer_norm_estimation_samples; + extended_stabilizer_norm_estimation_repetitions = other.extended_stabilizer_norm_estimation_repetitions; + extended_stabilizer_parallel_threshold = other.extended_stabilizer_parallel_threshold; + extended_stabilizer_probabilities_snapshot_samples = other.extended_stabilizer_probabilities_snapshot_samples; + // # MPS options + matrix_product_state_truncation_threshold = other.matrix_product_state_truncation_threshold; + if (other.matrix_product_state_max_bond_dimension.has_value()) matrix_product_state_max_bond_dimension.value(other.matrix_product_state_max_bond_dimension.value()); + mps_sample_measure_algorithm = other.mps_sample_measure_algorithm; + mps_log_data = other.mps_log_data; + mps_swap_direction = other.mps_swap_direction; + chop_threshold = other.chop_threshold; + mps_parallel_threshold = other.mps_parallel_threshold; + mps_omp_threads = other.mps_omp_threads; + // # tensor network options + tensor_network_num_sampling_qubits = other.tensor_network_num_sampling_qubits; + use_cuTensorNet_autotuning = other.use_cuTensorNet_autotuning; + // system configurations + param_table = other.param_table; + library_dir = other.library_dir; + if (other.n_qubits.has_value()) n_qubits.value(other.n_qubits.value()); + global_phase = other.global_phase; + memory_slots = other.memory_slots; + if (other._parallel_experiments.has_value()) _parallel_experiments.value(other._parallel_experiments.value()); + if (other._parallel_shots.has_value()) _parallel_shots.value(other._parallel_shots.value()); + if (other._parallel_state_update.has_value()) _parallel_state_update.value(other._parallel_state_update.value()); + if (other._parallel_experiments.has_value()) _parallel_experiments.value(other._parallel_experiments.value()); + if (other.fusion_allow_kraus.has_value()) fusion_allow_kraus.value(other.fusion_allow_kraus.value()); + if (other.fusion_allow_superop.has_value()) fusion_allow_superop.value(other.fusion_allow_superop.value()); + if (other.fusion_parallelization_threshold.has_value()) fusion_parallelization_threshold.value(other.fusion_parallelization_threshold.value()); + if (other._fusion_enable_n_qubits.has_value()) _fusion_enable_n_qubits.value(other._fusion_enable_n_qubits.value()); + if (other._fusion_enable_n_qubits_1.has_value()) _fusion_enable_n_qubits_1.value(other._fusion_enable_n_qubits_1.value()); + if (other._fusion_enable_n_qubits_2.has_value()) _fusion_enable_n_qubits_2.value(other._fusion_enable_n_qubits_2.value()); + if (other._fusion_enable_n_qubits_3.has_value()) _fusion_enable_n_qubits_3.value(other._fusion_enable_n_qubits_3.value()); + if (other._fusion_enable_n_qubits_4.has_value()) _fusion_enable_n_qubits_4.value(other._fusion_enable_n_qubits_4.value()); + if (other._fusion_enable_n_qubits_5.has_value()) _fusion_enable_n_qubits_5.value(other._fusion_enable_n_qubits_5.value()); + if (other._fusion_enable_diagonal.has_value()) _fusion_enable_diagonal.value(other._fusion_enable_diagonal.value()); + if (other._fusion_min_qubit.has_value()) _fusion_min_qubit.value(other._fusion_min_qubit.value()); + if (other.fusion_cost_factor.has_value()) fusion_cost_factor.value(other.fusion_cost_factor.value()); + + if (other.superoperator_parallel_threshold.has_value()) superoperator_parallel_threshold.value(other.superoperator_parallel_threshold.value()); + if (other.unitary_parallel_threshold.has_value()) unitary_parallel_threshold.value(other.unitary_parallel_threshold.value()); + if (other.memory_blocking_bits.has_value()) memory_blocking_bits.value(other.memory_blocking_bits.value()); + if (other.extended_stabilizer_norm_estimation_default_samples.has_value()) extended_stabilizer_norm_estimation_default_samples.value(other.extended_stabilizer_norm_estimation_default_samples.value()); + } +}; + +// Json conversion function +inline void from_json(const json_t& js, Config &config) { + get_value(config.shots, "shots", js); + get_value(config.method, "method", js); + get_value(config.device, "device", js); + get_value(config.precision, "precision", js); + // executor=None, + // max_job_size=None, + // max_shot_size=None, + get_value(config.enable_truncation, "enable_truncation", js); + get_value(config.zero_threshold, "zero_threshold", js); + get_value(config.validation_threshold, "validation_threshold", js); + get_value(config.max_parallel_threads, "max_parallel_threads", js); + get_value(config.max_parallel_experiments, "max_parallel_experiments", js); + get_value(config.max_parallel_shots, "max_parallel_shots", js); + get_value(config.max_parallel_shots, "max_parallel_shots", js); + get_value(config.fusion_enable, "fusion_enable", js); + get_value(config.fusion_verbose, "fusion_verbose", js); + get_value(config.fusion_max_qubit, "fusion_max_qubit", js); + get_value(config.fusion_threshold, "fusion_threshold", js); + get_value(config.accept_distributed_results, "accept_distributed_results", js); + get_value(config.memory, "memory", js); + // noise_model=None, + get_value(config.seed_simulator, "seed_simulator", js); + // # cuStateVec (cuQuantum) option + get_value(config.cuStateVec_enable, "cuStateVec_enable", js); + // # cache blocking for multi-GPUs/MPI options + get_value(config.blocking_qubits, "blocking_qubits", js); + get_value(config.blocking_enable, "blocking_enable", js); + get_value(config.chunk_swap_buffer_qubits, "chunk_swap_buffer_qubits", js); + // # multi-shots optimization options (GPU only) + get_value(config.batched_shots_gpu, "batched_shots_gpu", js); + get_value(config.batched_shots_gpu_max_qubits, "batched_shots_gpu_max_qubits", js); + get_value(config.num_threads_per_device, "num_threads_per_device", js); + // # statevector options + get_value(config.statevector_parallel_threshold, "statevector_parallel_threshold", js); + get_value(config.statevector_sample_measure_opt, "statevector_sample_measure_opt", js); + // # stabilizer options + get_value(config.stabilizer_max_snapshot_probabilities, "stabilizer_max_snapshot_probabilities", js); + // # extended stabilizer options + get_value(config.extended_stabilizer_sampling_method, "extended_stabilizer_sampling_method", js); + get_value(config.extended_stabilizer_metropolis_mixing_time, "extended_stabilizer_metropolis_mixing_time", js); + get_value(config.extended_stabilizer_approximation_error, "extended_stabilizer_approximation_error", js); + get_value(config.extended_stabilizer_norm_estimation_samples, "extended_stabilizer_norm_estimation_samples", js); + get_value(config.extended_stabilizer_norm_estimation_repetitions, "extended_stabilizer_norm_estimation_repetitions", js); + get_value(config.extended_stabilizer_parallel_threshold, "extended_stabilizer_parallel_threshold", js); + get_value(config.extended_stabilizer_probabilities_snapshot_samples, "extended_stabilizer_probabilities_snapshot_samples", js); + // # MPS options + get_value(config.matrix_product_state_truncation_threshold, "matrix_product_state_truncation_threshold", js); + get_value(config.matrix_product_state_max_bond_dimension, "matrix_product_state_max_bond_dimension", js); + get_value(config.mps_sample_measure_algorithm, "mps_sample_measure_algorithm", js); + get_value(config.mps_log_data, "mps_log_data", js); + get_value(config.mps_swap_direction, "mps_swap_direction", js); + get_value(config.chop_threshold, "chop_threshold", js); + get_value(config.mps_parallel_threshold, "mps_parallel_threshold", js); + get_value(config.mps_omp_threads, "mps_omp_threads", js); + // # tensor network options + get_value(config.tensor_network_num_sampling_qubits, "tensor_network_num_sampling_qubits", js); + get_value(config.use_cuTensorNet_autotuning, "use_cuTensorNet_autotuning", js); + // system configurations + get_value(config.param_table, "parameterizations", js); + get_value(config.library_dir, "library_dir", js); + get_value(config.global_phase, "global_phase", js); + get_value(config.memory_slots, "memory_slots", js); + + get_value(config.memory_slots, "_parallel_experiments", js); + get_value(config.memory_slots, "_parallel_shots", js); + get_value(config.memory_slots, "_parallel_state_update", js); + + get_value(config.fusion_allow_kraus, "fusion_allow_kraus", js); + get_value(config.fusion_allow_superop, "fusion_allow_superop", js); + get_value(config.fusion_parallelization_threshold, "fusion_parallelization_threshold", js); + get_value(config._fusion_enable_n_qubits, "_fusion_enable_n_qubits", js); + get_value(config._fusion_enable_n_qubits_1, "_fusion_enable_n_qubits_1", js); + get_value(config._fusion_enable_n_qubits_2, "_fusion_enable_n_qubits_2", js); + get_value(config._fusion_enable_n_qubits_3, "_fusion_enable_n_qubits_3", js); + get_value(config._fusion_enable_n_qubits_4, "_fusion_enable_n_qubits_4", js); + get_value(config._fusion_enable_n_qubits_5, "_fusion_enable_n_qubits_5", js); + get_value(config._fusion_enable_diagonal, "_fusion_enable_diagonal", js); + get_value(config._fusion_min_qubit, "_fusion_min_qubit", js); + get_value(config.fusion_cost_factor, "fusion_cost_factor", js); + + get_value(config.superoperator_parallel_threshold, "superoperator_parallel_threshold", js); + get_value(config.unitary_parallel_threshold, "unitary_parallel_threshold", js); + get_value(config.memory_blocking_bits, "memory_blocking_bits", js); + get_value(config.extended_stabilizer_norm_estimation_default_samples, "extended_stabilizer_norm_estimation_default_samples", js); +} + +} + +#endif \ No newline at end of file diff --git a/src/framework/linalg/vector.hpp b/src/framework/linalg/vector.hpp index e5f0d59e01..6098943a52 100755 --- a/src/framework/linalg/vector.hpp +++ b/src/framework/linalg/vector.hpp @@ -29,11 +29,11 @@ namespace AER { -template T *malloc_array(size_t size) { +template T *malloc_data(size_t size) { return reinterpret_cast(malloc(sizeof(T) * size)); } -template T *calloc_array(size_t size) { +template T *calloc_data(size_t size) { return reinterpret_cast(calloc(size, sizeof(T))); } @@ -192,11 +192,11 @@ template class Vector { template Vector::Vector(size_t sz, bool fill) - : size_(sz), data_((fill) ? calloc_array(size_) : malloc_array(size_)) {} + : size_(sz), data_((fill) ? calloc_data(size_) : malloc_data(size_)) {} template Vector::Vector(const Vector &other) - : size_(other.size_), data_(malloc_array(other.size_)) { + : size_(other.size_), data_(malloc_data(other.size_)) { std::copy(other.data_, other.data_ + other.size_, data_); } @@ -224,7 +224,7 @@ template Vector &Vector::operator=(const Vector &other) { if (size_ != other.size_) { free(data_); size_ = other.size_; - data_ = malloc_array(size_); + data_ = malloc_data(size_); } std::copy(other.data_, other.data_ + size_, data_); return *this; @@ -237,7 +237,7 @@ inline Vector &Vector::operator=(const Vector &other) { if (size_ != other.size_) { free(data_); size_ = other.size_; - data_ = malloc_array(size_); + data_ = malloc_data(size_); } std::transform(other.data_, other.data_ + size_, data_, [](const S &i) { return T{i}; }); @@ -252,7 +252,7 @@ template Vector Vector::copy_from_buffer(size_t sz, const T *buffer) { Vector ret; ret.size_ = sz; - ret.data_ = malloc_array(ret.size_); + ret.data_ = malloc_data(ret.size_); std::copy(buffer, buffer + ret.size_, ret.data_); return ret; } @@ -266,7 +266,7 @@ Vector Vector::move_from_buffer(size_t sz, T *buffer) { } template T *Vector::copy_to_buffer() const { - T *buffer = malloc_array(size_); + T *buffer = malloc_data(size_); std::copy(data_, data_ + size_, buffer); return buffer; } @@ -295,7 +295,7 @@ template void Vector::swap(Vector &other) { template void Vector::resize(size_t sz) { if (size_ == sz) return; - T *tmp = calloc_array(sz); + T *tmp = calloc_data(sz); std::move(data_, data_ + size_, tmp); free(data_); size_ = sz; @@ -317,7 +317,7 @@ Vector Vector::operator+(const Vector &other) const { } Vector result; result.size_ = size_; - result.data_ = malloc_array(size_); + result.data_ = malloc_data(size_); std::transform(data_, data_ + size_, other.data_, result.data_, [](const T &a, const T &b) -> T { return a + b; }); return result; @@ -339,7 +339,7 @@ Vector Vector::operator-(const Vector &other) const { } Vector result; result.size_ = size_; - result.data_ = malloc_array(size_); + result.data_ = malloc_data(size_); std::transform(data_, data_ + size_, other.data_, result.data_, [](const T &a, const T &b) -> T { return a - b; }); return result; @@ -357,7 +357,7 @@ template Vector &Vector::operator-=(const Vector &other) { template Vector Vector::operator*(const T &other) const { Vector ret; ret.size_ = size_; - ret.data_ = malloc_array(size_); + ret.data_ = malloc_data(size_); std::transform(data_, data_ + size_, ret.data_, [&other](const T &a) -> T { return a * other; }); return ret; @@ -372,7 +372,7 @@ template Vector &Vector::operator*=(const T &other) { template Vector Vector::operator/(const T &other) const { Vector ret; ret.size_ = size_; - ret.data_ = malloc_array(size_); + ret.data_ = malloc_data(size_); std::transform(data_, data_ + size_, ret.data_, [&other](const T &a) -> T { return a / other; }); return ret; diff --git a/src/framework/operations.hpp b/src/framework/operations.hpp index 5bf8b713e0..c1eb65c2fe 100755 --- a/src/framework/operations.hpp +++ b/src/framework/operations.hpp @@ -333,12 +333,25 @@ inline void check_duplicate_qubits(const Op &op) { // Generator functions //------------------------------------------------------------------------------ -inline Op make_unitary(const reg_t &qubits, const cmatrix_t &mat, std::string label = "") { +inline Op make_initialize(const reg_t &qubits, const std::vector &init_data) { + Op op; + op.type = OpType::initialize; + op.name = "initialize"; + op.qubits = qubits; + op.params = init_data; + return op; +} + +inline Op make_unitary(const reg_t &qubits, const cmatrix_t &mat, const int_t conditional = -1, std::string label = "") { Op op; op.type = OpType::matrix; op.name = "unitary"; op.qubits = qubits; op.mats = {mat}; + if (conditional >= 0) { + op.conditional = true; + op.conditional_reg = conditional; + } if (label != "") op.string_params = {label}; return op; @@ -356,7 +369,20 @@ inline Op make_unitary(const reg_t &qubits, cmatrix_t &&mat, std::string label = return op; } -inline Op make_diagonal(const reg_t &qubits, cvector_t &&vec, std::string label = "") { +inline Op make_diagonal(const reg_t &qubits, const cvector_t &vec, const std::string label = "") { + Op op; + op.type = OpType::diagonal_matrix; + op.name = "diagonal"; + op.qubits = qubits; + op.params = vec; + + if (label != "") + op.string_params = {label}; + + return op; +} + +inline Op make_diagonal(const reg_t &qubits, cvector_t &&vec, const std::string label = "") { Op op; op.type = OpType::diagonal_matrix; op.name = "diagonal"; @@ -369,12 +395,16 @@ inline Op make_diagonal(const reg_t &qubits, cvector_t &&vec, std::string label return op; } -inline Op make_superop(const reg_t &qubits, const cmatrix_t &mat) { +inline Op make_superop(const reg_t &qubits, const cmatrix_t &mat, const int_t conditional = -1) { Op op; op.type = OpType::superop; op.name = "superop"; op.qubits = qubits; op.mats = {mat}; + if (conditional >= 0) { + op.conditional = true; + op.conditional_reg = conditional; + } return op; } @@ -388,12 +418,16 @@ inline Op make_superop(const reg_t &qubits, cmatrix_t &&mat) { return op; } -inline Op make_kraus(const reg_t &qubits, const std::vector &mats) { +inline Op make_kraus(const reg_t &qubits, const std::vector &mats, const int_t conditional = -1) { Op op; op.type = OpType::kraus; op.name = "kraus"; op.qubits = qubits; op.mats = mats; + if (conditional >= 0) { + op.conditional = true; + op.conditional_reg = conditional; + } return op; } @@ -424,6 +458,71 @@ inline Op make_roerror(const reg_t &memory, std::vector &&probs) { return op; } +inline Op make_bfunc(const std::string &mask, const std::string &val, const std::string &relation, const uint_t regidx) { + Op op; + op.type = OpType::bfunc; + op.name = "bfunc"; + + op.string_params.resize(2); + op.string_params[0] = mask; + op.string_params[1] = val; + + // Load single register + op.registers.push_back(regidx); + + // Format hex strings + Utils::format_hex_inplace(op.string_params[0]); + Utils::format_hex_inplace(op.string_params[1]); + + const stringmap_t comp_table({ + {"==", RegComparison::Equal}, + {"!=", RegComparison::NotEqual}, + {"<", RegComparison::Less}, + {"<=", RegComparison::LessEqual}, + {">", RegComparison::Greater}, + {">=", RegComparison::GreaterEqual}, + }); + + auto it = comp_table.find(relation); + if (it == comp_table.end()) { + std::stringstream msg; + msg << "Invalid bfunc relation string :\"" << it->first << "\"." << std::endl; + throw std::invalid_argument(msg.str()); + } else { + op.bfunc = it->second; + } + + return op; + +} + +Op make_gate(const std::string &name, + const reg_t &qubits, + const std::vector ¶ms, + const std::vector &string_params, + const int_t conditional, + const std::string &label) { + Op op; + op.type = OpType::gate; + op.name = name; + op.qubits = qubits; + op.params = params; + + if (string_params.size() > 0) + op.string_params = string_params; + else if (label != "") + op.string_params = {label}; + else + op.string_params = {op.name}; + + if (conditional >= 0) { + op.conditional = true; + op.conditional_reg = conditional; + } + + return op; +} + template // real or complex numeric type inline Op make_u1(uint_t qubit, T lam) { Op op; @@ -467,6 +566,7 @@ inline Op make_reset(const reg_t & qubits, uint_t state = 0) { inline Op make_multiplexer(const reg_t &qubits, const std::vector &mats, + const int_t conditional = -1, std::string label = "") { // Check matrices are N-qubit @@ -511,6 +611,11 @@ inline Op make_multiplexer(const reg_t &qubits, if (label != "") op.string_params = {label}; + if (conditional >= 0) { + op.conditional = true; + op.conditional_reg = conditional; + } + // Validate qubits are unique. check_empty_qubits(op); check_duplicate_qubits(op); @@ -518,6 +623,210 @@ inline Op make_multiplexer(const reg_t &qubits, return op; } +inline Op make_save_state(const reg_t &qubits, + const std::string &name, + const std::string &snapshot_type, + const std::string &label) { + Op op; + op.name = name; + + // Get subtype + static const std::unordered_map types { + {"save_state", OpType::save_state}, + {"save_statevector", OpType::save_statevec}, + {"save_statevector_dict", OpType::save_statevec_dict}, + {"save_amplitudes", OpType::save_amps}, + {"save_amplitudes_sq", OpType::save_amps_sq}, + {"save_clifford", OpType::save_clifford}, + {"save_probabilities", OpType::save_probs}, + {"save_probabilities_dict", OpType::save_probs_ket}, + {"save_matrix_product_state", OpType::save_mps}, + {"save_unitary", OpType::save_unitary}, + {"save_superop", OpType::save_superop}, + {"save_density_matrix", OpType::save_densmat}, + {"save_stabilizer", OpType::save_stabilizer}, + {"save_expval", OpType::save_expval}, + {"save_expval_var", OpType::save_expval_var} + }; + + auto type_it = types.find(name); + if (type_it == types.end()) { + throw std::runtime_error("Invalid data type \"" + name + + "\" in save data instruction."); + } + op.type = type_it->second; + + // Get subtype + static const std::unordered_map subtypes { + {"single", DataSubType::single}, + {"c_single", DataSubType::c_single}, + {"average", DataSubType::average}, + {"c_average", DataSubType::c_average}, + {"list", DataSubType::list}, + {"c_list", DataSubType::c_list}, + {"accum", DataSubType::accum}, + {"c_accum", DataSubType::c_accum}, + }; + + auto subtype_it = subtypes.find(snapshot_type); + if (subtype_it == subtypes.end()) { + throw std::runtime_error("Invalid data subtype \"" + snapshot_type + + "\" in save data instruction."); + } + op.save_type = subtype_it->second; + + op.string_params.emplace_back(label); + + op.qubits = qubits; + + return op; +} + +inline Op make_save_amplitudes(const reg_t &qubits, + const std::string &name, + const std::vector &base_type, + const std::string &snapshot_type, + const std::string &label) { + auto op = make_save_state(qubits, name, snapshot_type, label); + op.int_params = base_type; + return op; +} + +inline Op make_save_expval(const reg_t &qubits, + const std::string &name, + const std::vector pauli_strings, + const std::vector coeff_reals, + const std::vector coeff_imags, + const std::string &snapshot_type, + const std::string &label) { + + assert(pauli_strings.size() == coeff_reals.size()); + assert(pauli_strings.size() == coeff_imags.size()); + + auto op = make_save_state(qubits, name, snapshot_type, label); + + for (uint_t i = 0; i < pauli_strings.size(); ++i) + op.expval_params.emplace_back(pauli_strings[i], coeff_reals[i], coeff_imags[i]); + + if (op.expval_params.empty()) { + std::string pauli(op.qubits.size(), 'I'); + op.expval_params.emplace_back(pauli, 0., 0.); + } + return op; +} + +template +inline Op make_set_vector(const reg_t &qubits, const std::string &name, const inputdata_t ¶ms) { + Op op; + // Get type + static const std::unordered_map types { + {"set_statevector", OpType::set_statevec}, + }; + auto type_it = types.find(name); + if (type_it == types.end()) { + throw std::runtime_error("Invalid data type \"" + name + + "\" in set data instruction."); + } + op.type = type_it->second; + op.name = name; + op.qubits = qubits; + op.params = Parser::template get_list_elem>(params, 0); + return op; +} + +template +inline Op make_set_matrix(const reg_t &qubits, const std::string &name, const inputdata_t ¶ms) { + Op op; + // Get type + static const std::unordered_map types { + {"set_density_matrix", OpType::set_densmat}, + {"set_unitary", OpType::set_unitary}, + {"set_superop", OpType::set_superop} + }; + auto type_it = types.find(name); + if (type_it == types.end()) { + throw std::runtime_error("Invalid data type \"" + name + + "\" in set data instruction."); + } + op.type = type_it->second; + op.name = name; + op.qubits = qubits; + op.mats.push_back(Parser::template get_list_elem(params, 0)); + return op; +} + +template +inline Op make_set_mps(const reg_t &qubits, const std::string &name, const inputdata_t ¶ms) { + Op op; + op.type = OpType::set_mps; + op.name = name; + op.qubits = qubits; + op.mps = Parser::template get_list_elem(params, 0); + return op; +} + +template +inline Op make_set_clifford(const reg_t &qubits, const std::string &name, const inputdata_t ¶ms) { + Op op; + op.type = OpType::set_stabilizer; + op.name = name; + op.qubits = qubits; + op.clifford = Parser::template get_list_elem(params, 0); + return op; +} + +inline Op make_jump(const reg_t &qubits, const std::vector ¶ms, const int_t conditional) { + Op op; + op.type = OpType::jump; + op.name = "jump"; + op.qubits = qubits; + op.string_params = params; + if (op.string_params.empty()) + throw std::invalid_argument(std::string("Invalid jump (\"params\" field missing).")); + + if (conditional >= 0) { + op.conditional = true; + op.conditional_reg = conditional; + } + + return op; +} + +inline Op make_mark(const reg_t &qubits, const std::vector ¶ms) { + Op op; + op.type = OpType::mark; + op.name = "mark"; + op.qubits = qubits; + op.string_params = params; + if (op.string_params.empty()) + throw std::invalid_argument(std::string("Invalid mark (\"params\" field missing).")); + + return op; +} + +inline Op make_measure(const reg_t &qubits, const reg_t &memory, const reg_t ®isters) { + Op op; + op.type = OpType::measure; + op.name = "measure"; + op.qubits = qubits; + op.memory = memory; + op.registers = registers; + return op; +} + +inline Op make_qerror_loc(const reg_t &qubits, const std::string &label, const int_t conditional = -1) { + Op op; + op.type = OpType::qerror_loc; + op.name = label; + op.qubits = qubits; + if (conditional >= 0) { + op.conditional = true; + op.conditional_reg = conditional; + } + return op; +} + + //------------------------------------------------------------------------------ // JSON conversion //------------------------------------------------------------------------------ @@ -1048,7 +1357,7 @@ Op input_to_op_multiplexer(const inputdata_t& input) { Parser::get_value(mats, "params", input); Parser::get_value(label, "label", input); // Construct op - auto op = make_multiplexer(qubits, mats, label); + auto op = make_multiplexer(qubits, mats, -1, label); // Conditional add_conditional(Allowed::Yes, op, input); return op; diff --git a/src/framework/pybind_json.hpp b/src/framework/pybind_json.hpp index de06755d73..6aa9dc89da 100755 --- a/src/framework/pybind_json.hpp +++ b/src/framework/pybind_json.hpp @@ -221,6 +221,7 @@ json_t JSON::iterable_to_json_list(const py::handle& obj){ void std::to_json(json_t &js, const py::handle &obj) { static py::object PyNoiseModel = py::module::import("qiskit_aer.noise.noise_model").attr("NoiseModel"); static py::object PyQasmQobj = py::module::import("qiskit.qobj.qasm_qobj").attr("QasmQobj"); + static py::object PyQasmQobjHeader = py::module::import("qiskit.qobj.common").attr("QobjExperimentHeader"); if (py::isinstance(obj)) { js = obj.cast(); } else if (py::isinstance(obj)) { @@ -245,6 +246,8 @@ void std::to_json(json_t &js, const py::handle &obj) { std::to_json(js, obj.attr("to_dict")()); } else if (py::isinstance(obj, PyQasmQobj)){ std::to_json(js, obj.attr("to_dict")()); + } else if (py::isinstance(obj, PyQasmQobjHeader)){ + std::to_json(js, obj.attr("to_dict")()); } else { auto type_str = std::string(py::str(obj.get_type())); if ( type_str == "" diff --git a/src/framework/results/experiment_result.hpp b/src/framework/results/experiment_result.hpp index c0668c6c81..7c0c842afc 100644 --- a/src/framework/results/experiment_result.hpp +++ b/src/framework/results/experiment_result.hpp @@ -19,6 +19,7 @@ #include "framework/results/data/metadata.hpp" #include "framework/creg.hpp" #include "framework/opset.hpp" +#include "framework/config.hpp" namespace AER { @@ -53,7 +54,7 @@ struct ExperimentResult { json_t to_json(); // Set the output data config options - void set_config(const json_t &config); + void set_config(const Config &config); // Combine stored data ExperimentResult& combine(ExperimentResult &&other); @@ -109,7 +110,7 @@ ExperimentResult& ExperimentResult::combine(ExperimentResult &&other) { return *this; } -void ExperimentResult::set_config(const json_t &config) { +void ExperimentResult::set_config(const Config &config) { } json_t ExperimentResult::to_json() { diff --git a/src/simulators/density_matrix/densitymatrix_state.hpp b/src/simulators/density_matrix/densitymatrix_state.hpp index ff586488bc..c2adbce113 100644 --- a/src/simulators/density_matrix/densitymatrix_state.hpp +++ b/src/simulators/density_matrix/densitymatrix_state.hpp @@ -100,7 +100,7 @@ class State : public QuantumState::StateChunk { // Load the threshold for applying OpenMP parallelization // if the controller/engine allows threads for it - virtual void set_config(const json_t &config) override; + virtual void set_config(const Config &config) override; // Sample n-measurement outcomes without applying the measure operation // to the system state @@ -514,20 +514,19 @@ size_t State::required_memory_mb( } template -void State::set_config(const json_t &config) +void State::set_config(const Config &config) { BaseState::set_config(config); // Set threshold for truncating snapshots - JSON::get_value(json_chop_threshold_, "chop_threshold", config); + json_chop_threshold_ = config.chop_threshold; uint_t i; for(i=0;i const std::vector &ops) const override; - void set_config(const json_t &config) override; + void set_config(const Config &config) override; std::vector sample_measure(const reg_t& qubits, uint_t shots, @@ -246,28 +246,29 @@ void State::initialize_qreg(uint_t num_qubits) BaseState::qreg_.initialize_omp(BaseState::threads_, omp_threshold_rank_); } -void State::set_config(const json_t &config) +void State::set_config(const Config &config) { // Set the error upper bound in the stabilizer rank approximation - JSON::get_value(approximation_error_, "extended_stabilizer_approximation_error", config); + approximation_error_ = config.extended_stabilizer_approximation_error; // Set the number of samples used in the norm estimation routine - JSON::get_value(norm_estimation_samples_, "extended_stabilizer_norm_estimation_default_samples", config); + if (config.extended_stabilizer_norm_estimation_default_samples.has_value()) + norm_estimation_samples_ = config.extended_stabilizer_norm_estimation_default_samples.value(); // Set the desired number of repetitions of the norm estimation step. If not explicitly set, we // compute a default basd on the approximation error norm_estimation_repetitions_ = std::llrint(std::log2(1. / approximation_error_)); - JSON::get_value(norm_estimation_repetitions_, "extended_stabilizer_norm_estimation_repetitions", config); + norm_estimation_repetitions_ = config.extended_stabilizer_norm_estimation_repetitions; // Set the number of steps used in the metropolis sampler before we // consider the distribution as approximating the output - JSON::get_value(metropolis_mixing_steps_, "extended_stabilizer_metropolis_mixing_time", config); + metropolis_mixing_steps_ = config.extended_stabilizer_metropolis_mixing_time; //Set the threshold of the decomposition before we use omp - JSON::get_value(omp_threshold_rank_, "extended_stabilizer_parallel_threshold", config); + omp_threshold_rank_ = config.extended_stabilizer_parallel_threshold; //Set the truncation threshold for the probabilities snapshot. - JSON::get_value(snapshot_chop_threshold_, "zero_threshold", config); + snapshot_chop_threshold_ = config.zero_threshold; //Set the number of samples for the probabilities snapshot - JSON::get_value(probabilities_snapshot_samples_, "extended_stabilizer_probabilities_snapshot_samples", config); + probabilities_snapshot_samples_ = config.extended_stabilizer_probabilities_snapshot_samples; //Set the measurement strategy std::string sampling_method_str = "resampled_metropolis"; - JSON::get_value(sampling_method_str, "extended_stabilizer_sampling_method", config); + sampling_method_str = config.extended_stabilizer_sampling_method; if (sampling_method_str == "metropolis") { sampling_method_ = SamplingMethod::metropolis; } diff --git a/src/simulators/matrix_product_state/matrix_product_state.hpp b/src/simulators/matrix_product_state/matrix_product_state.hpp index 012f7cab71..5c98d14186 100644 --- a/src/simulators/matrix_product_state/matrix_product_state.hpp +++ b/src/simulators/matrix_product_state/matrix_product_state.hpp @@ -116,7 +116,7 @@ class State : public QuantumState::State { // Load the threshold for applying OpenMP parallelization // if the controller/engine allows threads for it // We currently set the threshold to 1 in qasm_controller.hpp, i.e., no parallelization - virtual void set_config(const json_t &config) override; + virtual void set_config(const Config &config) override; virtual void add_metadata(ExperimentResult &result) const override; @@ -339,63 +339,39 @@ size_t State::required_memory_mb(uint_t num_qubits, return mem_mb; } -void State::set_config(const json_t &config) { +void State::set_config(const Config &config) { // Set threshold for truncating Schmidt coefficients - double threshold; - if (JSON::get_value(threshold, "matrix_product_state_truncation_threshold", config)) - MPS_Tensor::set_truncation_threshold(threshold); - else - MPS_Tensor::set_truncation_threshold(1e-16); + MPS_Tensor::set_truncation_threshold(config.matrix_product_state_truncation_threshold); - uint_t max_bond_dimension; - if (JSON::get_value(max_bond_dimension, "matrix_product_state_max_bond_dimension", config)) - MPS_Tensor::set_max_bond_dimension(max_bond_dimension); + if (config.matrix_product_state_max_bond_dimension.has_value()) + MPS_Tensor::set_max_bond_dimension(config.matrix_product_state_max_bond_dimension.value()); else MPS_Tensor::set_max_bond_dimension(UINT64_MAX); // Set threshold for truncating snapshots - uint_t json_chop_threshold; - if (JSON::get_value(json_chop_threshold, "chop_threshold", config)) - MPS::set_json_chop_threshold(json_chop_threshold); - else - MPS::set_json_chop_threshold(1E-8); + MPS::set_json_chop_threshold(config.chop_threshold); // Set OMP num threshold - uint_t omp_qubit_threshold; - if (JSON::get_value(omp_qubit_threshold, "mps_parallel_threshold", config)) - MPS::set_omp_threshold(omp_qubit_threshold); - else - MPS::set_omp_threshold(14); + MPS::set_omp_threshold(config.mps_parallel_threshold); // Set OMP threads - uint_t omp_threads; - if (JSON::get_value(omp_threads, "mps_omp_threads", config)) - MPS::set_omp_threads(omp_threads); + MPS::set_omp_threads(config.mps_omp_threads); + + // Set the algorithm for sample measure + if (config.mps_sample_measure_algorithm.compare("mps_probabilities") == 0) + MPS::set_sample_measure_alg(Sample_measure_alg::PROB); else - MPS::set_omp_threads(1); - -// Set the algorithm for sample measure - std::string alg; - if (JSON::get_value(alg, "mps_sample_measure_algorithm", config)) { - if (alg.compare("mps_probabilities") == 0) { - MPS::set_sample_measure_alg(Sample_measure_alg::PROB); - } else { - MPS::set_sample_measure_alg(Sample_measure_alg::APPLY_MEASURE); - } - } + MPS::set_sample_measure_alg(Sample_measure_alg::APPLY_MEASURE); + // Set mps_log_data - bool mps_log_data; - if (JSON::get_value(mps_log_data, "mps_log_data", config)) - MPS::set_mps_log_data(mps_log_data); + MPS::set_mps_log_data(config.mps_log_data); // Set the direction for the internal swaps std::string direction; - if (JSON::get_value(direction, "mps_swap_direction", config)) { - if (direction.compare("mps_swap_right") == 0) - MPS::set_mps_swap_direction(MPS_swap_direction::SWAP_RIGHT); - else - MPS::set_mps_swap_direction(MPS_swap_direction::SWAP_LEFT); - } + if (config.mps_swap_direction.compare("mps_swap_right") == 0) + MPS::set_mps_swap_direction(MPS_swap_direction::SWAP_RIGHT); + else + MPS::set_mps_swap_direction(MPS_swap_direction::SWAP_LEFT); } void State::add_metadata(ExperimentResult &result) const { diff --git a/src/simulators/stabilizer/stabilizer_state.hpp b/src/simulators/stabilizer/stabilizer_state.hpp index cb3fb36453..b236d0b9cc 100644 --- a/src/simulators/stabilizer/stabilizer_state.hpp +++ b/src/simulators/stabilizer/stabilizer_state.hpp @@ -17,6 +17,7 @@ #include "framework/utils.hpp" #include "framework/json.hpp" +#include "framework/config.hpp" #include "simulators/state.hpp" #include "clifford.hpp" @@ -84,7 +85,7 @@ class State : public QuantumState::State { const override; // Load any settings for the State class from a config JSON - virtual void set_config(const json_t &config) override; + virtual void set_config(const Config &config) override; // Sample n-measurement outcomes without applying the measure operation // to the system state @@ -235,12 +236,12 @@ size_t State::required_memory_mb(uint_t num_qubits, return mem; } -void State::set_config(const json_t &config) { +void State::set_config(const Config &config) { // Set threshold for truncating snapshots - JSON::get_value(json_chop_threshold_, "zero_threshold", config); + json_chop_threshold_ = config.zero_threshold; // Load max snapshot qubit size and set hard limit of 64 qubits. - JSON::get_value(max_qubits_snapshot_probs_, "stabilizer_max_snapshot_probabilities", config); + max_qubits_snapshot_probs_ = config.stabilizer_max_snapshot_probabilities; max_qubits_snapshot_probs_ = std::max(max_qubits_snapshot_probs_, 64); } diff --git a/src/simulators/state.hpp b/src/simulators/state.hpp index 4baa9e431a..6eb3a8a90f 100644 --- a/src/simulators/state.hpp +++ b/src/simulators/state.hpp @@ -20,6 +20,7 @@ #include "framework/types.hpp" #include "framework/creg.hpp" #include "framework/results/experiment_result.hpp" +#include "framework/config.hpp" #include "noise/noise_model.hpp" @@ -168,8 +169,8 @@ class Base { // Optional: Load config settings //----------------------------------------------------------------------- - // Load any settings for the State class from a config JSON - virtual void set_config(const json_t &config); + // Load any settings for the State class from a config + virtual void set_config(const Config &config); //----------------------------------------------------------------------- // Optional: Add information to metadata @@ -260,9 +261,9 @@ class Base { }; -void Base::set_config(const json_t &config) +void Base::set_config(const Config &config) { - JSON::get_value(sim_device_name_, "device", config); + sim_device_name_ = config.device; } std::vector Base::sample_measure(const reg_t &qubits, diff --git a/src/simulators/state_chunk.hpp b/src/simulators/state_chunk.hpp index dc2583830a..be0728212e 100644 --- a/src/simulators/state_chunk.hpp +++ b/src/simulators/state_chunk.hpp @@ -149,8 +149,8 @@ class StateChunk : public State { // Optional: Load config settings //----------------------------------------------------------------------- - // Load any settings for the StateChunk class from a config JSON - virtual void set_config(const json_t &config); + // Load any settings for the StateChunk class from a config + virtual void set_config(const Config &config); //======================================================================= // Standard non-virtual methods @@ -455,24 +455,21 @@ StateChunk::~StateChunk(void) } template -void StateChunk::set_config(const json_t &config) +void StateChunk::set_config(const Config &config) { BaseState::set_config(config); num_threads_per_group_ = 1; - if(JSON::check_key("num_threads_per_device", config)) { - JSON::get_value(num_threads_per_group_, "num_threads_per_device", config); - } + if (config.num_threads_per_device.has_value()) + num_threads_per_group_ = config.num_threads_per_device.value(); - if(JSON::check_key("chunk_swap_buffer_qubits", config)) { - JSON::get_value(chunk_swap_buffer_qubits_, "chunk_swap_buffer_qubits", config); - } + if (config.chunk_swap_buffer_qubits.has_value()) + chunk_swap_buffer_qubits_ = config.chunk_swap_buffer_qubits.value(); #ifdef AER_CUSTATEVEC //cuStateVec configs - if(JSON::check_key("cuStateVec_enable", config)) { - JSON::get_value(cuStateVec_enable_, "cuStateVec_enable", config); - } + if (config.cuStateVec_enable) + cuStateVec_enable_ = config.cuStateVec_enable.value(); #endif } diff --git a/src/simulators/statevector/statevector_state.hpp b/src/simulators/statevector/statevector_state.hpp index 6c2d5d3f19..ea3c5e9d06 100755 --- a/src/simulators/statevector/statevector_state.hpp +++ b/src/simulators/statevector/statevector_state.hpp @@ -22,6 +22,7 @@ #include "framework/json.hpp" #include "framework/utils.hpp" +#include "framework/config.hpp" #include "simulators/state_chunk.hpp" #include "qubitvector.hpp" #ifdef AER_THRUST_SUPPORTED @@ -107,7 +108,7 @@ class State : public QuantumState::StateChunk { // Load the threshold for applying OpenMP parallelization // if the controller/engine allows threads for it - virtual void set_config(const json_t &config) override; + virtual void set_config(const Config &config) override; // Sample n-measurement outcomes without applying the measure operation // to the system state @@ -506,21 +507,21 @@ size_t State::required_memory_mb(uint_t num_qubits, } template -void State::set_config(const json_t &config) { +void State::set_config(const Config &config) { BaseState::set_config(config); // Set threshold for truncating states to be saved - JSON::get_value(json_chop_threshold_, "zero_threshold", config); + json_chop_threshold_ = config.zero_threshold; for(int_t i=0;i { // Load the threshold for applying OpenMP parallelization // if the controller/engine allows threads for it // Config: {"omp_qubit_threshold": 3} - virtual void set_config(const json_t &config) override; + virtual void set_config(const Config &config) override; virtual bool allocate(uint_t num_qubits,uint_t block_bits,uint_t num_parallel_shots = 1) override; @@ -290,13 +291,13 @@ size_t State::required_memory_mb( return mem_mb; } -template void State::set_config(const json_t &config) { +template void State::set_config(const Config &config) { // Set OMP threshold for state update functions - JSON::get_value(omp_qubit_threshold_, "superoperator_parallel_threshold", - config); + if (config.superoperator_parallel_threshold.has_value()) + omp_qubit_threshold_ = config.superoperator_parallel_threshold.value(); // Set threshold for truncating snapshots - JSON::get_value(json_chop_threshold_, "zero_threshold", config); + json_chop_threshold_ = config.zero_threshold; BaseState::qreg_.set_json_chop_threshold(json_chop_threshold_); } diff --git a/src/simulators/tensor_network/tensor_net_state.hpp b/src/simulators/tensor_network/tensor_net_state.hpp index 4e2f4aa91d..1c99895287 100644 --- a/src/simulators/tensor_network/tensor_net_state.hpp +++ b/src/simulators/tensor_network/tensor_net_state.hpp @@ -23,6 +23,7 @@ #include "framework/json.hpp" #include "framework/opset.hpp" #include "framework/utils.hpp" +#include "framework/config.hpp" #include "simulators/state_chunk.hpp" #include "simulators/tensor_network/tensor_net.hpp" @@ -108,7 +109,7 @@ class State : public QuantumState::State { // Load the threshold for applying OpenMP parallelization // if the controller/engine allows threads for it - void set_config(const json_t &config) override; + void set_config(const Config &config) override; // Initializes an n-qubit state to the all |0> state void initialize_qreg(const uint_t num_qubits) override; @@ -399,17 +400,13 @@ size_t State::required_memory_mb(uint_t num_qubits, } template -void State::set_config(const json_t &config) +void State::set_config(const Config &config) { // Set threshold for truncating snapshots - JSON::get_value(json_chop_threshold_, "zero_threshold", config); + json_chop_threshold_ = config.zero_threshold; - if(JSON::check_key("tensor_network_num_sampling_qubits", config)) { - JSON::get_value(num_sampling_qubits_, "tensor_network_num_sampling_qubits", config); - } - if(JSON::check_key("use_cuTensorNet_autotuning", config)) { - JSON::get_value(use_cuTensorNet_autotuning_, "use_cuTensorNet_autotuning", config); - } + num_sampling_qubits_ = config.tensor_network_num_sampling_qubits; + use_cuTensorNet_autotuning_ = config.use_cuTensorNet_autotuning; } diff --git a/src/simulators/unitary/unitary_state.hpp b/src/simulators/unitary/unitary_state.hpp index 7d397b4768..064411369d 100755 --- a/src/simulators/unitary/unitary_state.hpp +++ b/src/simulators/unitary/unitary_state.hpp @@ -21,6 +21,7 @@ #include "simulators/state.hpp" #include "framework/json.hpp" #include "framework/utils.hpp" +#include "framework/config.hpp" #include "simulators/state_chunk.hpp" #include "unitarymatrix.hpp" #ifdef AER_THRUST_SUPPORTED @@ -98,7 +99,7 @@ class State : public virtual QuantumState::StateChunk { // Load the threshold for applying OpenMP parallelization // if the controller/engine allows threads for it // Config: {"omp_qubit_threshold": 7} - virtual void set_config(const json_t &config) override; + virtual void set_config(const Config &config) override; //----------------------------------------------------------------------- // Additional methods @@ -361,15 +362,16 @@ size_t State::required_memory_mb( } template -void State::set_config(const json_t &config) +void State::set_config(const Config &config) { BaseState::set_config(config); // Set OMP threshold for state update functions - JSON::get_value(omp_qubit_threshold_, "unitary_parallel_threshold", config); + if (config.unitary_parallel_threshold.has_value()) + omp_qubit_threshold_ = config.unitary_parallel_threshold.value(); // Set threshold for truncating snapshots - JSON::get_value(json_chop_threshold_, "zero_threshold", config); + json_chop_threshold_ = config.zero_threshold; for(int_t i=0;i= 1){ blocking_enabled_ = true; } - if (JSON::check_key("memory_blocking_bits", config_)){ - JSON::get_value(memory_blocking_bits_, "memory_blocking_bits", config_); + if (config.memory_blocking_bits.has_value()) { + memory_blocking_bits_ = config.memory_blocking_bits.value(); if(memory_blocking_bits_ >= 10){ //blocking qubit should be <=10 memory_blocking_bits_ = 10; } } - std::string method; - if (JSON::get_value(method, "method", config)) { - if(method.find("density_matrix") != std::string::npos){ - density_matrix_ = true; - } - } + std::string method = config.method; + if(method.find("density_matrix") != std::string::npos) + density_matrix_ = true; } diff --git a/src/transpile/circuitopt.hpp b/src/transpile/circuitopt.hpp index 268ea0354d..6e7aa32547 100644 --- a/src/transpile/circuitopt.hpp +++ b/src/transpile/circuitopt.hpp @@ -19,6 +19,7 @@ #include #include "framework/opset.hpp" +#include "framework/config.hpp" #include "noise/noise_model.hpp" @@ -42,13 +43,13 @@ class CircuitOptimization { const Operations::OpSet &opset, ExperimentResult &result) const = 0; - virtual void set_config(const json_t &config); + virtual void set_config(const Config &config); protected: - json_t config_; + Config config_; }; -void CircuitOptimization::set_config(const json_t& config) { +void CircuitOptimization::set_config(const Config& config) { config_ = config; } diff --git a/src/transpile/fusion.hpp b/src/transpile/fusion.hpp index 2ae2f0e960..3f884d453b 100644 --- a/src/transpile/fusion.hpp +++ b/src/transpile/fusion.hpp @@ -19,6 +19,7 @@ #include "transpile/circuitopt.hpp" #include "framework/avx2_detect.hpp" +#include "framework/config.hpp" #include "simulators/unitary/unitary_state.hpp" #include "simulators/superoperator/superoperator_state.hpp" @@ -268,7 +269,7 @@ class Fuser { public: virtual std::string name() const = 0; - virtual void set_config(const json_t &config) = 0; + virtual void set_config(const Config &config) = 0; virtual void set_metadata(ExperimentResult &result) const { }; //nop @@ -308,7 +309,7 @@ class CostBasedFusion : public Fuser { virtual std::string name() const override { return "cost_base"; }; - virtual void set_config(const json_t &config) override; + virtual void set_config(const Config &config) override; virtual void set_metadata(ExperimentResult &result) const override; @@ -338,11 +339,10 @@ class CostBasedFusion : public Fuser { template class NQubitFusion : public Fuser { public: - NQubitFusion(): opt_name(std::to_string(N) + "_qubits"), - activate_prop_name("fusion_enable." + std::to_string(N) + "_qubits") { + NQubitFusion(): opt_name(std::to_string(N) + "_qubits") { } - virtual void set_config(const json_t &config) override; + virtual void set_config(const Config &config) override; virtual std::string name() const override { return opt_name; @@ -359,17 +359,26 @@ class NQubitFusion : public Fuser { private: bool active = true; const std::string opt_name; - const std::string activate_prop_name; uint_t qubit_threshold = 5; }; template -void NQubitFusion::set_config(const json_t &config) { - if (JSON::check_key("fusion_enable.n_qubits", config)) - JSON::get_value(active, "fusion_enable.n_qubits", config); +void NQubitFusion::set_config(const Config &config) { + // For debuging + if (config._fusion_enable_n_qubits.has_value()) + active = config._fusion_enable_n_qubits.value(); + + if (N == 1 && config._fusion_enable_n_qubits_1.has_value()) + active = config._fusion_enable_n_qubits_1.value(); + else if (N == 2 && config._fusion_enable_n_qubits_2.has_value()) + active = config._fusion_enable_n_qubits_2.value(); + else if (N == 3 && config._fusion_enable_n_qubits_3.has_value()) + active = config._fusion_enable_n_qubits_3.value(); + else if (N == 4 && config._fusion_enable_n_qubits_4.has_value()) + active = config._fusion_enable_n_qubits_4.value(); + else if (N == 5 && config._fusion_enable_n_qubits_5.has_value()) + active = config._fusion_enable_n_qubits_5.value(); - if (JSON::check_key(activate_prop_name, config)) - JSON::get_value(active, activate_prop_name, config); } template @@ -468,7 +477,7 @@ class DiagonalFusion : public Fuser { virtual std::string name() const override { return "diagonal"; }; - virtual void set_config(const json_t &config) override; + virtual void set_config(const Config &config) override; virtual bool aggregate_operations(oplist_t& ops, const int fusion_start, @@ -486,11 +495,11 @@ class DiagonalFusion : public Fuser { bool active = true; }; -void DiagonalFusion::set_config(const json_t &config) { - if (JSON::check_key("fusion_enable.diagonal", config)) - JSON::get_value(active, "fusion_enable.diagonal", config); - if (JSON::check_key("fusion_min_qubit.diagonal", config)) - JSON::get_value(min_qubit, "fusion_min_qubit.diagonal", config); +void DiagonalFusion::set_config(const Config &config) { + if (config._fusion_enable_diagonal.has_value()) + active = config._fusion_enable_diagonal.value(); + if (config._fusion_min_qubit.has_value()) + min_qubit = config._fusion_min_qubit.value(); } bool DiagonalFusion::is_diagonal_op(const op_t& op) const { @@ -687,7 +696,7 @@ class Fusion : public CircuitOptimization { */ Fusion(); - void set_config(const json_t &config) override; + void set_config(const Config &config) override; virtual void set_parallelization(uint_t num) { parallelization_ = num; }; @@ -760,33 +769,30 @@ Fusion::Fusion() { fusers.push_back(std::make_shared()); } -void Fusion::set_config(const json_t &config) { +void Fusion::set_config(const Config &config) { CircuitOptimization::set_config(config); - if (JSON::check_key("fusion_verbose", config_)) - JSON::get_value(verbose, "fusion_verbose", config_); + verbose = config.fusion_verbose; + active = config.fusion_enable; - if (JSON::check_key("fusion_enable", config_)) - JSON::get_value(active, "fusion_enable", config_); + if (config.fusion_max_qubit.has_value()) + max_qubit = config.fusion_max_qubit.value(); - if (JSON::check_key("fusion_max_qubit", config_)) - JSON::get_value(max_qubit, "fusion_max_qubit", config_); - - if (JSON::check_key("fusion_threshold", config_)) - JSON::get_value(threshold, "fusion_threshold", config_); + if (config.fusion_threshold.has_value()) + threshold = config.fusion_threshold.value(); for (std::shared_ptr& fuser: fusers) fuser->set_config(config_); - if (JSON::check_key("fusion_allow_kraus", config)) - JSON::get_value(allow_kraus, "fusion_allow_kraus", config); + if (config.fusion_allow_kraus.has_value()) + allow_kraus = config.fusion_allow_kraus.value(); - if (JSON::check_key("fusion_allow_superop", config)) - JSON::get_value(allow_superop, "fusion_allow_superop", config); + if (config.fusion_allow_superop.has_value()) + allow_superop = config.fusion_allow_superop.value(); - if (JSON::check_key("fusion_parallelization_threshold", config_)) - JSON::get_value(parallel_threshold_, "fusion_parallelization_threshold", config_); + if (config.fusion_parallelization_threshold.has_value()) + parallel_threshold_ = config.fusion_parallelization_threshold.value(); } void Fusion::optimize_circuit(Circuit& circ, @@ -898,19 +904,35 @@ void CostBasedFusion::set_metadata(ExperimentResult &result) const { result.metadata.add(cost_factor, "fusion", "cost_factor"); } -void CostBasedFusion::set_config(const json_t &config) { - - if (JSON::check_key("fusion_cost_factor", config)) - JSON::get_value(cost_factor, "fusion_cost_factor", config); +void CostBasedFusion::set_config(const Config &config) { + + if (config.fusion_cost_factor.has_value()) + cost_factor = config.fusion_cost_factor.value(); + + if (config._fusion_enable_cost_based.has_value()) + active = config._fusion_enable_cost_based.value(); + + if (config._fusion_cost_1.has_value()) + costs_[0] = config._fusion_cost_1.value(); + if (config._fusion_cost_2.has_value()) + costs_[1] = config._fusion_cost_2.value(); + if (config._fusion_cost_3.has_value()) + costs_[2] = config._fusion_cost_3.value(); + if (config._fusion_cost_4.has_value()) + costs_[3] = config._fusion_cost_4.value(); + if (config._fusion_cost_5.has_value()) + costs_[4] = config._fusion_cost_5.value(); + if (config._fusion_cost_6.has_value()) + costs_[5] = config._fusion_cost_6.value(); + if (config._fusion_cost_7.has_value()) + costs_[6] = config._fusion_cost_7.value(); + if (config._fusion_cost_8.has_value()) + costs_[7] = config._fusion_cost_8.value(); + if (config._fusion_cost_9.has_value()) + costs_[8] = config._fusion_cost_9.value(); + if (config._fusion_cost_10.has_value()) + costs_[9] = config._fusion_cost_10.value(); - if (JSON::check_key("fusion_enable.cost_based", config)) - JSON::get_value(active, "fusion_enable.cost_based", config); - - for (int i = 0; i < 64; ++i) { - auto prop_name = "fusion_cost." + std::to_string(i + 1); - if (JSON::check_key(prop_name, config)) - JSON::get_value(costs_[i], prop_name, config); - } } bool CostBasedFusion::aggregate_operations(oplist_t& ops, diff --git a/test/terra/backends/aer_simulator/test_auto_method.py b/test/terra/backends/aer_simulator/test_auto_method.py index f193d34707..2772a5f663 100644 --- a/test/terra/backends/aer_simulator/test_auto_method.py +++ b/test/terra/backends/aer_simulator/test_auto_method.py @@ -213,21 +213,3 @@ def test_auto_method_nonclifford_circuit_and_kraus_noise(self): result = backend.run(circuits, shots=shots).result() success = getattr(result, 'success', False) self.compare_result_metadata(result, circuits, 'method', "density_matrix") - - def test_auto_method_partial_result_a_single_invalid_circuit(self): - """Test a partial result is returned with a job with a valid and invalid circuit.""" - circuits = [] - qc = QuantumCircuit(2) - qc.h(0) - qc.cx(0, 1) - qc.measure_all() - qc_2 = QuantumVolume(5) - qc_2.measure_all() - circuits.append(qc_2) - circuits.append(qc) - backend = self.backend() - shots = 100 - result = backend.run(circuits, shots=shots).result() - self.assertEqual(result.status, 'PARTIAL COMPLETED') - self.assertTrue(hasattr(result.results[1].data, 'counts')) - self.assertFalse(hasattr(result.results[0].data, 'counts')) diff --git a/test/terra/backends/aer_simulator/test_circuit.py b/test/terra/backends/aer_simulator/test_circuit.py new file mode 100644 index 0000000000..cf26bd3a2f --- /dev/null +++ b/test/terra/backends/aer_simulator/test_circuit.py @@ -0,0 +1,169 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2018, 2019. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. +""" +AerSimulator Integration Tests +""" +from math import sqrt +from ddt import ddt +from qiskit import ClassicalRegister, QuantumCircuit, QuantumRegister +from qiskit.circuit import CircuitInstruction +from test.terra.reference import ref_algorithms + +from test.terra.backends.simulator_test_case import ( + SimulatorTestCase, supported_methods) + + +@ddt +class TestVariousCircuit(SimulatorTestCase): + """AerSimulator tests to simulate various types of circuits""" + + @supported_methods( + ['automatic', 'statevector', 'density_matrix', + 'matrix_product_state', 'extended_stabilizer', 'tensor_network']) + def test_quantum_register_circuit(self, method, device): + """Test circuits with quantum registers.""" + + qubits = QuantumRegister(3) + clbits = ClassicalRegister(3) + + circuit = QuantumCircuit(qubits, clbits) + circuit.h(qubits[0]) + circuit.cx(qubits[0], qubits[1]) + circuit.cx(qubits[0], qubits[2]) + + for q, c in zip(qubits, clbits): + circuit.measure(q, c) + + backend = self.backend( + method=method, + device=device, + seed_simulator=1111 + ) + + shots=1000 + result = backend.run(circuit, shots=shots).result() + self.assertSuccess(result) + self.compare_counts(result, [circuit], [{'0x0':500, '0x7':500}], delta=0.05 * shots) + + @supported_methods( + ['automatic', 'statevector', 'density_matrix', + 'matrix_product_state', 'extended_stabilizer', 'tensor_network']) + def test_qubits_circuit(self, method, device): + """Test circuits with quantum registers.""" + + qubits = QuantumRegister(3) + clbits = ClassicalRegister(3) + + circuit = QuantumCircuit() + circuit.add_bits(qubits) + circuit.add_bits(clbits) + circuit.h(qubits[0]) + circuit.cx(qubits[0], qubits[1]) + circuit.cx(qubits[0], qubits[2]) + + for q, c in zip(qubits, clbits): + circuit.measure(q, c) + + backend = self.backend( + method=method, + device=device, + seed_simulator=1111 + ) + + shots=1000 + result = backend.run(circuit, shots=shots).result() + self.assertSuccess(result) + self.compare_counts(result, [circuit], [{'0x0':500, '0x7':500}], delta=0.05 * shots) + + @supported_methods( + ['automatic', 'statevector', 'density_matrix', + 'matrix_product_state', 'extended_stabilizer', 'tensor_network']) + def test_qubits_quantum_register_circuit(self, method, device): + """Test circuits with quantum registers.""" + + qubits0 = QuantumRegister(2) + clbits1 = ClassicalRegister(2) + qubits1 = QuantumRegister(1) + clbits2 = ClassicalRegister(1) + + circuit = QuantumCircuit(qubits0, clbits1) + circuit.add_bits(qubits1) + circuit.add_bits(clbits2) + circuit.h(qubits0[0]) + circuit.cx(qubits0[0], qubits0[1]) + circuit.cx(qubits0[0], qubits1[0]) + + for qubits, clbits in zip([qubits0, qubits1], [clbits1, clbits2]): + for q, c in zip(qubits, clbits): + circuit.measure(q, c) + + backend = self.backend( + method=method, + device=device, + seed_simulator=1111 + ) + + shots=1000 + result = backend.run(circuit, shots=shots).result() + self.assertSuccess(result) + self.compare_counts(result, [circuit], [{'0x0':500, '0x7':500}], delta=0.05 * shots) + + qubits0 = QuantumRegister(1) + clbits1 = ClassicalRegister(1) + qubits1 = QuantumRegister(1) + clbits2 = ClassicalRegister(1) + qubits2 = QuantumRegister(1) + clbits3 = ClassicalRegister(1) + + circuit = QuantumCircuit(qubits0, clbits1) + circuit.add_bits(qubits1) + circuit.add_bits(clbits2) + circuit.add_register(qubits2) + circuit.add_register(clbits3) + circuit.h(qubits0[0]) + circuit.cx(qubits0[0], qubits1[0]) + circuit.cx(qubits1[0], qubits2[0]) + + for qubits, clbits in zip([qubits0, qubits1, qubits2], [clbits1, clbits2, clbits3]): + for q, c in zip(qubits, clbits): + circuit.measure(q, c) + + backend = self.backend( + method=method, + device=device, + seed_simulator=1111 + ) + + shots=1000 + result = backend.run(circuit, shots=shots).result() + self.assertSuccess(result) + self.compare_counts(result, [circuit], [{'0x0':500, '0x7':500}], delta=0.05 * shots) + + def test_partial_result_a_single_invalid_circuit(self): + """Test a partial result is returned with a job with a valid and invalid circuit.""" + + circuits = [] + qc = QuantumCircuit(2) + qc.h(0) + qc.cx(0, 1) + qc.measure_all() + qc_2 = QuantumCircuit(50) + qc_2.h(range(50)) + qc_2.measure_all() + circuits.append(qc_2) + circuits.append(qc) + backend = self.backend() + shots = 100 + result = backend.run(circuits, shots=shots, method='statevector').result() + self.assertEqual(result.status, 'PARTIAL COMPLETED') + self.assertTrue(hasattr(result.results[1].data, 'counts')) + self.assertFalse(hasattr(result.results[0].data, 'counts')) diff --git a/test/terra/backends/aer_simulator/test_conditional.py b/test/terra/backends/aer_simulator/test_conditional.py index c7ee731362..a541e58b7e 100644 --- a/test/terra/backends/aer_simulator/test_conditional.py +++ b/test/terra/backends/aer_simulator/test_conditional.py @@ -92,6 +92,8 @@ def test_conditional_gates_132bit(self, method, device): conditional_type='gate') targets = ref_conditionals.condtional_counts_nbit(132, cases, shots, hex_counts=False) + circuits = circuits[0:1] + targets = targets[0:1] result = backend.run(circuits, shots=shots).result() self.assertSuccess(result) self.compare_counts(result, circuits, targets, hex_counts=False, delta=0) diff --git a/test/terra/backends/simulator_test_case.py b/test/terra/backends/simulator_test_case.py index 914a3e8b29..4094077682 100644 --- a/test/terra/backends/simulator_test_case.py +++ b/test/terra/backends/simulator_test_case.py @@ -20,7 +20,7 @@ from test.terra.common import QiskitAerTestCase from qiskit.circuit import QuantumCircuit from qiskit.compiler import assemble -from qiskit_aer.backends.backend_utils import cpp_execute +from qiskit_aer.backends.backend_utils import cpp_execute_qobj from qiskit_aer.backends.controller_wrappers import aer_controller_execute @@ -117,7 +117,7 @@ def check_cuStateVec(devices): device="GPU", cuStateVec_enable=True) #run dummy circuit to check if Aer is built with cuStateVec - result = cpp_execute(aer_controller_execute(), qobj) + result = cpp_execute_qobj(aer_controller_execute(), qobj) return result.get('success', False) else: return False