Skip to content

Commit

Permalink
Merge pull request #1996 from Ericgig/dims.rework
Browse files Browse the repository at this point in the history
Dimensions class
  • Loading branch information
Ericgig committed Nov 21, 2023
2 parents 4ce8829 + 80c6aad commit bc8b264
Show file tree
Hide file tree
Showing 30 changed files with 1,260 additions and 763 deletions.
13 changes: 10 additions & 3 deletions doc/apidoc/functions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,21 @@ Quantum States
--------------

.. automodule:: qutip.core.states
:members: basis, bell_state, bra, coherent, coherent_dm, enr_state_dictionaries, enr_thermal_dm, enr_fock, fock, fock_dm, ghz_state, maximally_mixed_dm, ket, ket2dm, phase_basis, projection, qutrit_basis, singlet_state, spin_state, spin_coherent, state_number_enumerate, state_number_index, state_index_number, state_number_qobj, thermal_dm, triplet_states, w_state, zero_ket
:members: basis, bell_state, bra, coherent, coherent_dm, fock, fock_dm, ghz_state, maximally_mixed_dm, ket, ket2dm, phase_basis, projection, qutrit_basis, singlet_state, spin_state, spin_coherent, state_number_enumerate, state_number_index, state_index_number, state_number_qobj, thermal_dm, triplet_states, w_state, zero_ket


Quantum Operators
-----------------

.. automodule:: qutip.core.operators
:members: charge, commutator, create, destroy, displace, enr_destroy, enr_identity, fcreate, fdestroy, jmat, num, qeye, identity, momentum, phase, position, qdiags, qutrit_ops, qzero, sigmam, sigmap, sigmax, sigmay, sigmaz, spin_Jx, spin_Jy, spin_Jz, spin_Jm, spin_Jp, squeeze, squeezing, tunneling
:members: charge, commutator, create, destroy, displace, fcreate, fdestroy, jmat, num, qeye, identity, momentum, phase, position, qdiags, qutrit_ops, qzero, sigmam, sigmap, sigmax, sigmay, sigmaz, spin_Jx, spin_Jy, spin_Jz, spin_Jm, spin_Jp, squeeze, squeezing, tunneling


Energy Restricted Operators
---------------------------

.. automodule:: qutip.core.energy_restricted
:members: enr_state_dictionaries, enr_thermal_dm, enr_fock, enr_destroy, enr_identity


.. _functions-rand:
Expand Down Expand Up @@ -54,7 +61,7 @@ Operators and Superoperator Dimensions
--------------------------------------

.. automodule:: qutip.core.dimensions
:members: is_scalar, is_vector, is_vectorized_oper, type_from_dims, flatten, deep_remove, unflatten, collapse_dims_oper, collapse_dims_super, enumerate_flat, deep_map, dims_to_tensor_perm, dims_to_tensor_shape, dims_idxs_to_tensor_idxs
:members: flatten, deep_remove, unflatten, collapse_dims_oper, collapse_dims_super, enumerate_flat, deep_map, dims_to_tensor_perm, dims_to_tensor_shape, dims_idxs_to_tensor_idxs


Functions acting on states and operators
Expand Down
1 change: 1 addition & 0 deletions doc/changes/1996.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Create a Dimension class
1 change: 1 addition & 0 deletions qutip/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from .superop_reps import *
from .subsystem_apply import *
from .blochredfield import *
from .energy_restricted import *
from . import gates

del cy # File in cy are not public facing
3 changes: 1 addition & 2 deletions qutip/core/_brtensor.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -266,8 +266,7 @@ cdef class _BlochRedfieldElement(_BaseElement):
raise ValueError('Invalid tensortype')

cpdef object qobj(self, t):
return Qobj(self.data(t), dims=self.dims, type="super",
copy=False, superrep="super")
return Qobj(self.data(t), dims=self.dims, copy=False, superrep="super")

cpdef object coeff(self, t):
return 1.
Expand Down
4 changes: 1 addition & 3 deletions qutip/core/cy/qobjevo.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@ from qutip.core.data.base cimport idxint
cdef class QobjEvo:
cdef:
list elements
readonly list dims
readonly object _dims
readonly (idxint, idxint) shape
readonly str type
readonly str superrep
int _issuper
int _isoper

Expand Down
131 changes: 37 additions & 94 deletions qutip/core/cy/qobjevo.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,16 @@ from functools import partial
import qutip
from .. import Qobj
from .. import data as _data
from ..dimensions import Dimensions
from ..coefficient import coefficient, CompilationOptions
from ._element import *
from ..dimensions import type_from_dims
from qutip.settings import settings

from qutip.core.cy._element cimport _BaseElement
from qutip.core.data cimport Dense, Data, dense
from qutip.core.data.expect cimport *
from qutip.core.data.reshape cimport (column_stack_dense, column_unstack_dense)
from qutip.core.cy.coefficient cimport Coefficient
from qutip.core.qobj import _MATMUL_TYPE_LOOKUP
from libc.math cimport fabs

__all__ = ['QobjEvo']
Expand Down Expand Up @@ -135,7 +134,6 @@ cdef class QobjEvo:
QobjEvo(f, args={'w': 1j})
For list based :obj:`~QobjEvo`, the list must consist of :obj`~Qobj` or
``[Qobj, Coefficient]`` pairs:
Expand Down Expand Up @@ -192,11 +190,8 @@ cdef class QobjEvo:
order=3, copy=True, compress=True,
function_style=None, boundary_conditions=None):
if isinstance(Q_object, QobjEvo):
self.dims = Q_object.dims.copy()
self._dims = Q_object._dims
self.shape = Q_object.shape
self.type = Q_object.type
self._issuper = (<QobjEvo> Q_object)._issuper
self._isoper = (<QobjEvo> Q_object)._isoper
self.elements = (<QobjEvo> Q_object).elements.copy()
if args:
self.arguments(args)
Expand All @@ -205,10 +200,8 @@ cdef class QobjEvo:
return

self.elements = []
self.dims = None
self._dims = None
self.shape = (0, 0)
self._issuper = -1
self._isoper = -1
args = args or {}

if (
Expand Down Expand Up @@ -280,40 +273,25 @@ cdef class QobjEvo:
" received: {!r}".format(op)
)

if self.dims is None:
self.dims = qobj.dims
if self._dims is None:
self._dims = qobj._dims
self.shape = qobj.shape
self.type = qobj.type
self.superrep = qobj.superrep
elif self.dims != qobj.dims or self.shape != qobj.shape:
elif self._dims != qobj._dims:
raise ValueError(
f"QobjEvo term {op!r} has dims {qobj.dims!r} and shape"
f" {qobj.shape!r} but previous terms had dims {self.dims!r}"
f" and shape {self.shape!r}."
)
elif self.type != qobj.type:
raise ValueError(
f"QobjEvo term {op!r} has type {qobj.type!r} but "
f"previous terms had type {self.type!r}."
)
elif self.superrep != qobj.superrep:
raise ValueError(
f"QobjEvo term {op!r} has superrep {qobj.superrep!r} but "
f"previous terms had superrep {self.superrep!r}."
)

return out

@classmethod
def _restore(cls, elements, dims, shape, type, superrep, flags):
def _restore(cls, elements, dims, shape):
"""Recreate a QobjEvo without using __init__. """
cdef QobjEvo out = cls.__new__(cls)
out.elements = elements
out.dims = dims
out._dims = dims
out.shape = shape
out.type = type
out.superrep = superrep
out._issuper, out._isoper = flags
return out

def _getstate(self):
Expand All @@ -324,11 +302,8 @@ cdef class QobjEvo:
# etc., so we create our own.
return {
"elements": self.elements,
"dims": self.dims,
"dims": self._dims,
"shape": self.shape,
"type": self.type,
"superrep": self.superrep,
"flags": (self._issuper, self._isoper,)
}

def __call__(self, double t, dict _args=None, **kwargs):
Expand Down Expand Up @@ -379,8 +354,7 @@ cdef class QobjEvo:
out = _data.add(out, obj.data, coeff)

return Qobj(
out, dims=self.dims, copy=False, isherm=isherm or None,
type=self.type, superrep=self.superrep
out, dims=self._dims, copy=False, isherm=isherm or None,
)

cpdef Data _call(QobjEvo self, double t):
Expand Down Expand Up @@ -461,19 +435,19 @@ cdef class QobjEvo:
def __iadd__(QobjEvo self, other):
cdef _BaseElement element
if isinstance(other, QobjEvo):
if other.dims != self.dims:
if other._dims != self._dims:
raise TypeError("incompatible dimensions" +
str(self.dims) + ", " + str(other.dims))
for element in (<QobjEvo> other).elements:
self.elements.append(element)
elif isinstance(other, Qobj):
if other.dims != self.dims:
if other._dims != self._dims:
raise TypeError("incompatible dimensions" +
str(self.dims) + ", " + str(other.dims))
self.elements.append(_ConstantElement(other))
elif (
isinstance(other, numbers.Number) and
self.dims[0] == self.dims[1]
self._dims[0] == self._dims[1]
):
self.elements.append(_ConstantElement(other * qutip.qeye_like(self)))
else:
Expand Down Expand Up @@ -508,53 +482,31 @@ cdef class QobjEvo:
if isinstance(left, QobjEvo):
return left.copy().__imatmul__(right)
elif isinstance(left, Qobj):
if left.dims[1] != (<QobjEvo> right).dims[0]:
if left._dims[1] != (<QobjEvo> right)._dims[0]:
raise TypeError("incompatible dimensions" +
str(left.dims[1]) + ", " +
str((<QobjEvo> right).dims[0]))

type_ =_MATMUL_TYPE_LOOKUP.get((left.type, right.type))
if type_ is None:
raise TypeError(
"incompatible matmul types "
+ repr(left.type) + " and " + repr(right.type)
)

res = right.copy()
res.dims = [left.dims[0], right.dims[1]]
res._dims = Dimensions(left._dims[0], right._dims[1])
res.shape = (left.shape[0], right.shape[1])
res.type = type_
left = _ConstantElement(left)
res.elements = [left @ element for element in res.elements]
res._issuper = -1
res._isoper = -1
return res
else:
return NotImplemented

def __rmatmul__(QobjEvo self, other):
cdef QobjEvo res
if isinstance(other, Qobj):
if other.dims[1] != self.dims[0]:
if other._dims[1] != self._dims[0]:
raise TypeError("incompatible dimensions" +
str(other.dims[1]) + ", " +
str(self.dims[0]))

type_ =_MATMUL_TYPE_LOOKUP.get((other.type, self.type))
if type_ is None:
raise TypeError(
"incompatible matmul types "
+ repr(other.type) + " and " + repr(self.type)
)

res = self.copy()
res.dims = [other.dims[0], res.dims[1]]
res._dims = Dimensions([other._dims[0], res._dims[1]])
res.shape = (other.shape[0], res.shape[1])
res.type = type_
other = _ConstantElement(other)
res.elements = [other @ element for element in res.elements]
res._issuper = -1
res._isoper = -1
return res
else:
return NotImplemented
Expand All @@ -565,19 +517,8 @@ cdef class QobjEvo:
raise TypeError("incompatible dimensions" +
str(self.dims[1]) + ", " +
str(other.dims[0]))

type_ =_MATMUL_TYPE_LOOKUP.get((self.type, other.type))
if type_ is None:
raise TypeError(
"incompatible matmul types "
+ repr(self.type) + " and " + repr(other.type)
)

self.dims = [self.dims[0], other.dims[1]]
self._dims = Dimensions([self.dims[0], other.dims[1]])
self.shape = (self.shape[0], other.shape[1])
self.type = type_
self._issuper = -1
self._isoper = -1
if isinstance(other, Qobj):
other = _ConstantElement(other)
self.elements = [element @ other for element in self.elements]
Expand Down Expand Up @@ -734,11 +675,8 @@ cdef class QobjEvo:
raise TypeError("The op_mapping function must return a Qobj")
cdef QobjEvo res = self.copy()
res.elements = [element.linear_map(op_mapping) for element in res.elements]
res.dims = res.elements[0].qobj(0).dims
res._dims = res.elements[0].qobj(0)._dims
res.shape = res.elements[0].qobj(0).shape
res.type = res.elements[0].qobj(0).type
res._issuper = res.elements[0].qobj(0).issuper
res._isoper = res.elements[0].qobj(0).isoper
if not _skip_check:
if res(0) != out:
raise ValueError("The mapping is not linear")
Expand Down Expand Up @@ -855,19 +793,24 @@ cdef class QobjEvo:
@property
def isoper(self):
"""Indicates if the system represents an operator."""
# TODO: isoper should be part of dims
if self._isoper == -1:
self._isoper = type_from_dims(self.dims) == "oper"
return self._isoper
return self._dims.type == "oper"

@property
def issuper(self):
"""Indicates if the system represents a superoperator."""
# TODO: issuper should/will be part of dims
# remove self._issuper then
if self._issuper == -1:
self._issuper = type_from_dims(self.dims) == "super"
return self._issuper
return self._dims.issuper

@property
def dims(self):
return self._dims.as_list()

@property
def type(self):
return self._dims.type

@property
def superrep(self):
return self._dims.superrep

###########################################################################
# operation methods #
Expand Down Expand Up @@ -905,8 +848,8 @@ cdef class QobjEvo:
raise ValueError("Must be an operator or super operator to compute"
" an expectation value")
if not (
(self.dims[1] == state.dims[0]) or
(self.issuper and self.dims[1] == state.dims)
(self._dims[1] == state._dims[0]) or
(self.issuper and self._dims[1] == state._dims)
):
raise ValueError("incompatible dimensions " + str(self.dims) +
", " + str(state.dims))
Expand Down Expand Up @@ -994,12 +937,12 @@ cdef class QobjEvo:
if not isinstance(state, Qobj):
raise TypeError("A Qobj state is expected")

if self.dims[1] != state.dims[0]:
if self._dims[1] != state._dims[0]:
raise ValueError("incompatible dimensions " + str(self.dims[1]) +
", " + str(state.dims[0]))

return Qobj(self.matmul_data(t, state.data),
dims=[self.dims[0], state.dims[1]],
dims=[self._dims[0], state._dims[1]],
copy=False
)

Expand Down

0 comments on commit bc8b264

Please sign in to comment.