# Copyright 2018-2021 Xanadu Quantum Technologies Inc.# Licensed under the Apache License, Version 2.0 (the "License");# you may not use this file except in compliance with the License.# You may obtain a copy of the License at# http://www.apache.org/licenses/LICENSE-2.0# Unless required by applicable law or agreed to in writing, software# distributed under the License is distributed on an "AS IS" BASIS,# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.# See the License for the specific language governing permissions and# limitations under the License.r"""The default.mixed device is PennyLane's standard qubit simulator for mixed-state computations.It implements some built-in qubit :doc:`operations </introduction/operations>`,providing a simple mixed-state simulation ofqubit-based quantum circuits."""# isort: skip_file# pylint: disable=wrong-import-order, ungrouped-importsimportloggingimportnumpyasnpimportpennylaneasqmlfrompennylane.mathimportget_canonical_interface_namefrompennylane.loggingimportdebug_logger,debug_logger_init# We deliberately separate the imports to avoid confusion with the legacy deviceimportwarningsfromcollections.abcimportCallable,SequencefromdataclassesimportreplacefromtypingimportOptional,Unionfrompennylane.devices.qubit_mixedimportsimulatefrompennylane.ops.channelimport__qubit_channels__aschannelsfrompennylane.transforms.coreimportTransformProgramfrompennylane.tapeimportQuantumScriptfrompennylane.typingimportResult,ResultBatchfrom.importDevicefrom.execution_configimportExecutionConfigfrom.preprocessimport(decompose,no_sampling,null_postprocessing,validate_device_wires,validate_measurements,validate_observables,)from.modifiersimportsimulator_tracking,single_tape_supportlogger=logging.getLogger(__name__)logger.addHandler(logging.NullHandler())observables={"Hadamard","Hermitian","Identity","PauliX","PauliY","PauliZ","Prod","Projector","SparseHamiltonian","SProd","Sum",}operations={"Identity","Snapshot","BasisState","StatePrep","QubitDensityMatrix","QubitUnitary","ControlledQubitUnitary","BlockEncode","MultiControlledX","DiagonalQubitUnitary","SpecialUnitary","PauliX","PauliY","PauliZ","MultiRZ","Hadamard","S","T","SX","CNOT","SWAP","ISWAP","CSWAP","Toffoli","CCZ","CY","CZ","CH","PhaseShift","PCPhase","ControlledPhaseShift","CPhaseShift00","CPhaseShift01","CPhaseShift10","RX","RY","RZ","Rot","CRX","CRY","CRZ","CRot","AmplitudeDamping","GeneralizedAmplitudeDamping","PhaseDamping","DepolarizingChannel","BitFlip","PhaseFlip","PauliError","ResetError","QubitChannel","SingleExcitation","SingleExcitationPlus","SingleExcitationMinus","DoubleExcitation","DoubleExcitationPlus","DoubleExcitationMinus","QubitCarry","QubitSum","OrbitalRotation","FermionicSWAP","QFT","ThermalRelaxationError","ECR","ParametrizedEvolution","GlobalPhase",}
[docs]defobservable_stopping_condition(obs:qml.operation.Operator)->bool:"""Specifies whether an observable is accepted by DefaultQubitMixed."""ifobs.namein{"Prod","Sum"}:returnall(observable_stopping_condition(observable)forobservableinobs.operands)ifobs.name=="LinearCombination":returnall(observable_stopping_condition(observable)forobservableinobs.terms()[1])ifobs.name=="SProd":returnobservable_stopping_condition(obs.base)returnobs.nameinobservables
[docs]defstopping_condition(op:qml.operation.Operator)->bool:"""Specify whether an Operator object is supported by the device."""expected_set=operations|{"Snapshot"}|channelsreturnop.nameinexpected_set
[docs]@qml.transformdefwarn_readout_error_state(tape:qml.tape.QuantumTape,)->tuple[Sequence[qml.tape.QuantumTape],Callable]:"""If a measurement in the QNode is an analytic state or density_matrix, warn that readout error will not be applied. Args: tape (QuantumTape, .QNode, Callable): a quantum circuit. Returns: qnode (pennylane.QNode) or quantum function (callable) or tuple[List[.QuantumTape], function]: The unaltered input circuit. """ifnottape.shots:formintape.measurements:ifisinstance(m,qml.measurements.StateMP):warnings.warn(f"Measurement {m} is not affected by readout error.")return(tape,),null_postprocessing
[docs]@simulator_tracking@single_tape_supportclassDefaultMixed(Device):r"""A PennyLane Python-based device for mixed-state qubit simulation. Args: wires (int, Iterable[Number, str]): Number of wires present on the device, or iterable that contains unique labels for the wires as numbers (i.e., ``[-1, 0, 2]``) or strings (``['ancilla', 'q1', 'q2']``). shots (int, Sequence[int], Sequence[Union[int, Sequence[int]]]): The default number of shots to use in executions involving this device. seed (Union[str, None, int, array_like[int], SeedSequence, BitGenerator, Generator, jax.random.PRNGKey]): A seed-like parameter matching that of ``seed`` for ``numpy.random.default_rng``, or a request to seed from numpy's global random number generator. The default, ``seed="global"`` pulls a seed from NumPy's global generator. ``seed=None`` will pull a seed from the OS entropy. If a ``jax.random.PRNGKey`` is passed as the seed, a JAX-specific sampling function using ``jax.random.choice`` and the ``PRNGKey`` will be used for sampling rather than ``numpy.random.default_rng``. r_dtype (numpy.dtype): Real datatype to use for computations. Default is np.float64. c_dtype (numpy.dtype): Complex datatype to use for computations. Default is np.complex128. readout_prob (float): Probability of readout error for qubit measurements. Must be in :math:`[0,1]`. """_device_options=("rng","prng_key")# tuple of string names for all the device options.@propertydefname(self):"""The name of the device."""return"default.mixed"# pylint: disable=too-many-positional-arguments@debug_logger_initdef__init__(# pylint: disable=too-many-argumentsself,wires=None,shots=None,seed="global",readout_prob=None,)->None:ifisinstance(wires,int)andwires>23:raiseValueError("This device does not currently support computations on more than 23 wires")self.readout_err=readout_prob# Check that the readout error probability, if entered, is either integer or float in [0,1]ifself.readout_errisnotNone:ifnotisinstance(self.readout_err,float)andnotisinstance(self.readout_err,int):raiseTypeError("The readout error probability should be an integer or a floating-point number in [0,1].")ifself.readout_err<0orself.readout_err>1:raiseValueError("The readout error probability should be in the range [0,1].")super().__init__(wires=wires,shots=shots)# Seed settingseed=np.random.randint(0,high=10000000)ifseed=="global"elseseedifqml.math.get_interface(seed)=="jax":self._prng_key=seedself._rng=np.random.default_rng(None)else:self._prng_key=Noneself._rng=np.random.default_rng(seed)self._debugger=None
[docs]@debug_loggerdefsupports_derivatives(self,execution_config:Optional[ExecutionConfig]=None,circuit:Optional[QuantumScript]=None,)->bool:"""Check whether or not derivatives are available for a given configuration and circuit. ``DefaultQubitMixed`` supports backpropagation derivatives with analytic results. Args: execution_config (ExecutionConfig): The configuration of the desired derivative calculation. circuit (QuantumTape): An optional circuit to check derivatives support for. Returns: bool: Whether or not a derivative can be calculated provided the given information. """ifexecution_configisNoneorexecution_config.gradient_methodin{"backprop","best"}:returncircuitisNoneornotcircuit.shotsreturnFalse
def_setup_execution_config(self,execution_config:ExecutionConfig)->ExecutionConfig:"""This is a private helper for ``preprocess`` that sets up the execution config. Args: execution_config (ExecutionConfig): an unprocessed execution config. Returns: ExecutionConfig: a preprocessed execution config. """updated_values={}# Add gradient relatedifexecution_config.gradient_method=="best":updated_values["gradient_method"]="backprop"updated_values["use_device_gradient"]=execution_config.gradient_methodin{"backprop","best",}updated_values["grad_on_execution"]=Falseexecution_config.interface=get_canonical_interface_name(execution_config.interface)# Add device optionsupdated_values["device_options"]=dict(execution_config.device_options)# copyforoptioninexecution_config.device_options:ifoptionnotinself._device_options:raiseqml.DeviceError(f"device option {option} not present on {self}")foroptioninself._device_options:ifoptionnotinupdated_values["device_options"]:updated_values["device_options"][option]=getattr(self,f"_{option}")returnreplace(execution_config,**updated_values)
[docs]@debug_loggerdefpreprocess(self,execution_config:ExecutionConfig=None,)->tuple[TransformProgram,ExecutionConfig]:"""This function defines the device transform program to be applied and an updated device configuration. Args: execution_config (Union[ExecutionConfig, Sequence[ExecutionConfig]]): A data structure describing the parameters needed to fully describe the execution. Returns: TransformProgram, ExecutionConfig: A transform program that when called returns ``QuantumTape`` objects that the device can natively execute, as well as a postprocessing function to be called after execution, and a configuration with unset specifications filled in. This device: * Supports any qubit operations that provide a matrix * Supports any qubit channel that provides Kraus matrices """execution_config=execution_configorExecutionConfig()config=self._setup_execution_config(execution_config)transform_program=TransformProgram()# Defer first since it addes wires to the devicetransform_program.add_transform(qml.defer_measurements,allow_postselect=False)transform_program.add_transform(decompose,stopping_condition=stopping_condition,name=self.name,)# TODO: If the setup_execution_config method becomes circuit-dependent in the future,# we should handle this case directly within setup_execution_config. This would# eliminate the need for the no_sampling transform in this section.ifconfig.gradient_method=="backprop":transform_program.add_transform(no_sampling,name="backprop + default.mixed")ifself.readout_errisnotNone:transform_program.add_transform(warn_readout_error_state)# Add the validate sectiontransform_program.add_transform(validate_device_wires,self.wires,name=self.name)transform_program.add_transform(validate_measurements,analytic_measurements=qml.devices.default_qubit.accepted_analytic_measurement,sample_measurements=qml.devices.default_qubit.accepted_sample_measurement,name=self.name,)transform_program.add_transform(validate_observables,stopping_condition=observable_stopping_condition,name=self.name)returntransform_program,config