r"""
Contains the parent class of quantum systems and protocols.
Every system and protocol has a simulation, and this class creates this composition.
.. currentmodule:: quanguru.classes.QSimComp
.. autosummary::
QSimComp
.. |c| unicode:: U+2705
.. |x| unicode:: U+274C
.. |w| unicode:: U+2000
======================= ================== ================ ===============
**Function Name** **Docstrings** **Unit Tests** **Tutorials**
======================= ================== ================ ===============
`QSimComp` |w| |w| |w| |c| |w| |w| |c| |w| |w| |c|
======================= ================== ================ ===============
"""
from .baseClasses import computeBase
from .QSim import Simulation # pylint: disable=import-outside-toplevel
[docs]class QSimComp(computeBase):
r"""
Inhereted by the :class:`quantum systems <quanguru.classes.QSys.genericQSys>` and
:class:`protocols <quanguru.classes.QPro.genericProtocol>` and has a
simulation attribute which is an instance of :class:`Simulation <quanguru.classes.Simulation.Simulation>`. The goal
for such an attribute is to increase possible ways of running a
:class:`Simulation <quanguru.classes.Simulation.Simulation>`.
NOTE : This class branches the inheritance started by :class:`paramBoundBase`, and this branch extends to
:class:`quantum systems <quanguru.classes.QSys.genericQSys>` and
:class:`protocols <quanguru.classes.QPro.genericProtocol>`.
"""
#: (**class attribute**) class label used in default naming
label = 'QSimComp'
#: (**class attribute**) number of instances created internally by the library
_internalInstances: int = 0
#: (**class attribute**) number of instances created explicitly by the user
_externalInstances: int = 0
#: (**class attribute**) number of total instances = _internalInstances + _externalInstances
_instances: int = 0
__slots__ = ['__simulation']
def __init__(self, **kwargs) -> None:
super().__init__(_internal=kwargs.pop('_internal', False))
#: an instance of Simulation (as a protected attribute) to run
# ``self.simulation.run`` without any explicit Simulation creation and/or ``subSys`` addition call.
self.__simulation = Simulation(_internal=True, superSys=self)
self._QSimComp__simulation._paramBoundBase__paramBound[self.name] = self # pylint: disable=protected-access
self._named__setKwargs(**kwargs) # pylint: disable=no-member
# self.__openSystem = False
[docs] def __getattribute__(self, __name: str):
r"""
Custom ``__getattribute__`` method to get the parameters of the internal simulation object through self
"""
try:
obj = super().__getattribute__(__name)
except AttributeError as attErr1:
try:
obj = getattr(self.simulation, __name)
except AttributeError as exc:
raise attErr1 from exc
return obj
[docs] def __setattr__(self, __name: str, __value) -> None:
r"""
Custom ``__setattr__`` method to set the parameters of the internal simulation object through self
"""
try:
obj = super().__setattr__(__name, __value)
except AttributeError as attErr1:
try:
obj = setattr(self.simulation, __name, __value)
except AttributeError as exc:
raise attErr1 from exc
return obj
@property
def simulation(self):
r"""
``returns _QSimComp__simulation`` attribute (an instance of :class:`~Simulation`).
"""
return self._QSimComp__simulation
[docs] def runSimulation(self, p=None, coreCount=None):
r"""
an alternative to run the simulation, equivalent to ``self.simulation.run()``
"""
return self._QSimComp__simulation.run(p=p, coreCount=coreCount)
@property
def _initialStateInput(self):
return self.simulation._initialStateInput
@property
def simParameters(self):
r"""
returns a tuple contaning simulation parameters for the self.simulation, which might be bound to some other
simulation object and getting values from there.
There are various setters of this property with different names to set the relevant attribute of the simulation.
# TODO should we include calculate methods into the tuple ? and have a setter for calculates as well ?
"""
return ('totalTime:', self.simulation.totalTime, 'stepSize:', self.simulation.stepSize, 'stepCount:',
self.simulation.stepCount, 'samples:', self.simulation.samples, 'delStates:',
self.simulation.delStates, 'compute:', self.simulation.compute)
@simParameters.setter
def simTotalTime(self, fTime):
r"""
setter for the totalTime of Simulation, equivalent to ``self.simulation.totalTime = fTime``
"""
self.simulation.totalTime = fTime
@simParameters.setter
def simStepSize(self, stepsize):
r"""
setter for the stepSize of Simulation, equivalent to ``self.simulation.stepSize = stepsize``
"""
self.simulation.stepSize = stepsize
@simParameters.setter
def simStepCount(self, num):
r"""
setter for the stepCount of Simulation, equivalent to ``self.simulation.stepCount = num``
"""
self.simulation.stepCount = num
@simParameters.setter
def simSamples(self, num):
r"""
setter for the samples of Simulation, equivalent to ``self.simulation.samples = num``
"""
self.simulation.samples = num
@simParameters.setter
def simCompute(self, func):
r"""
setter for the compute of Simulation, equivalent to ``self.simulation.compute = func``
"""
self.simulation.compute = func
@simParameters.setter
def simDelStates(self, boolean):
r"""
setter for the delStates of Simulation, equivalent to ``self.simulation.delStates = boolean``
"""
self.simulation.delStates = boolean
@property
def initialState(self):
r"""
Getter for the simulation initial state, equivalent to ``self.simulation.initialState``.
This works by assuming that its setter/s makes sure that _stateBase__initialState.value is not None
for single systems (if its state is set).
"""
return self.simulation.initialState
[docs] def delMatrices(self, _exclude=[]): # pylint: disable=dangerous-default-value
r"""
This method extends :meth:`delMatrices <paramBoundBase.delMatrices>` of :class:`computeBase` to also call
:meth:`delMatrices <paramBoundBase.delMatrices>` on ``self.simulation``.
"""
if self not in _exclude:
_exclude = super().delMatrices(_exclude)
_exclude = self.simulation.delMatrices(_exclude)
return _exclude