Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dimensions class #1996

Merged
merged 27 commits into from
Nov 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
bb9ecde
Add Dimensions class to Qobj
Ericgig Aug 25, 2022
ea92672
Merge branch 'dev.major' of https://github.com/qutip/qutip into dims.…
Ericgig Sep 15, 2022
d70bc88
Merge branch 'dev.major' of https://github.com/qutip/qutip into dims.…
Ericgig Sep 16, 2022
027e65b
simpler qobj init
Ericgig Sep 19, 2022
a220e78
Fix doc build
Ericgig Sep 19, 2022
07a0244
Add docstrings
Ericgig Sep 19, 2022
5ef0152
type as property
Ericgig Sep 19, 2022
98a9c17
Merge remote-tracking branch 'upstream/master' into dims.rework
Feb 13, 2023
a975637
Add __all__ to dimensions.py
Ericgig Feb 14, 2023
87d9592
Merge branch 'master' of https://github.com/qutip/qutip into dims.rework
Ericgig Feb 27, 2023
c2ecbb3
Merge branch 'master' of https://github.com/qutip/qutip into dims.rework
Ericgig Jun 28, 2023
697d246
Add towncrier entry
Ericgig Jun 28, 2023
c8d5b97
Merge branch 'master' of https://github.com/qutip/qutip into dims.rework
Ericgig Aug 2, 2023
92ce62e
Merge branch 'master' of https://github.com/qutip/qutip into dims.rework
Ericgig Aug 4, 2023
f0f8e9a
Fix comments
Ericgig Aug 4, 2023
2f3974c
Apply suggestions from code review
Ericgig Aug 10, 2023
38800d8
improve code climate
Aug 10, 2023
009e605
Apply suggestions from code review
Ericgig Nov 15, 2023
9c72295
Improve from comments
Ericgig Nov 16, 2023
79e2d8d
Merge branch 'dims.rework' of https://github.com/Ericgig/qutip into d…
Ericgig Nov 16, 2023
d039094
Merge branch 'master' of https://github.com/qutip/qutip into dims.rework
Ericgig Nov 16, 2023
05037cc
Remove old type check functions
Ericgig Nov 16, 2023
a93c7d5
start removing Qobj(type)
Ericgig Nov 17, 2023
5c73919
start removing Qobj(type)
Ericgig Nov 17, 2023
7aa4bf0
Remove type setting
Ericgig Nov 17, 2023
3fb1322
Rm type and reshape in Qobj.__init__
Nov 20, 2023
80c6aad
Stricker list format test
Nov 20, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also have a means for the user to access the actual dims object?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To discuss, I do not like the list format much and would be fine with keeping it only for input and print.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd also be happy to only use the list format for input and printing the dims, but it would break some existing usage patterns (e.g. .dims[0]). This is v5 though, so maybe that is okay. Perhaps something we can discuss in the admin meeting or on the mailing 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