Skip to content

Commit

Permalink
Setting for isherm -> real
Browse files Browse the repository at this point in the history
  • Loading branch information
Ericgig committed Feb 19, 2024
1 parent 0695ad3 commit 75944b8
Show file tree
Hide file tree
Showing 6 changed files with 34 additions and 42 deletions.
12 changes: 8 additions & 4 deletions qutip/core/expect.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from .qobj import Qobj
from . import data as _data
from ..settings import settings


def expect(oper, state):
Expand Down Expand Up @@ -71,10 +72,13 @@ def _single_qobj_expect(oper, state):

# This ensures that expect can return something that is not a number such
# as a `tensorflow.Tensor` in qutip-tensorflow.
return out.real if (oper.isherm
and (state.isket or state.isherm)
and hasattr(out, "real")
) else out
if (
settings.core["auto_real_casting"]
and oper.isherm
and (state.isket or state.isherm)
):
out = out.real
return out


def variance(oper, state):
Expand Down
3 changes: 3 additions & 0 deletions qutip/core/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ class CoreOptions(QutipOptions):
"function_coefficient_style": "auto",
# Default Qobj dtype for Qobj create function
"default_dtype": None,
# Expect, trace, etc. will return real for hermitian matrices.
# Hermiticity checks can be slow, stop jitting, etc.
"auto_real_casting": True,
}
_settings_name = "core"

Expand Down
13 changes: 6 additions & 7 deletions qutip/core/qobj.py
Original file line number Diff line number Diff line change
Expand Up @@ -727,9 +727,9 @@ def tr(self):
out = _data.trace(self._data)
# This ensures that trace can return something that is not a number such
# as a `tensorflow.Tensor` in qutip-tensorflow.
return out.real if (self.isherm
and hasattr(out, "real")
) else out
if settings.core["auto_real_casting"] and self.isherm:
out = out.real
return out

def purity(self):
"""Calculate purity of a quantum object.
Expand Down Expand Up @@ -797,10 +797,9 @@ def diag(self):
"""
# TODO: add a `diagonal` method to the data layer?
out = _data.to(_data.CSR, self.data).as_scipy().diagonal()
if np.any(np.imag(out) > settings.core['atol']) or not self.isherm:
return out
else:
return np.real(out)
if settings.core["auto_real_casting"] and self.isherm:
out = np.real(out)
return out

def expm(self, dtype=_data.Dense):
"""Matrix exponential of quantum operator.
Expand Down
2 changes: 1 addition & 1 deletion qutip/solver/solver_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def _prepare_state(self, state):

self._state_metadata = {
'dims': state.dims,
'isherm': state.isherm and not (self.rhs.dims == state.dims)
'isherm': state._isherm if self.rhs.dims != state.dims else False
}
if self.rhs.dims[1] == state.dims:
return stack_columns(state.data)
Expand Down
18 changes: 4 additions & 14 deletions qutip/tests/core/test_expect.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,18 +162,8 @@ def test_compatibility_with_solver(solve):
np.testing.assert_allclose(np.array(direct_), indirect_, atol=1e-12)


def test_no_real_attribute(monkeypatch):
"""This tests ensures that expect still works even if the output of a
specialisation does not have the ``real`` attribute. This is the case for
the tensorflow and cupy data layers."""

def mocker_expect_return(oper, state):
"""
We simply return None which does not have the `real` attribute.
"""
return "object without .real"

monkeypatch.setattr(_data, "expect", mocker_expect_return)

def test_no_real_casting(monkeypatch):
sz = qutip.sigmaz() # the choice of the matrix does not matter
assert "object without .real" == qutip.expect(sz, sz)
assert isinstance(qutip.expect(sz, sz), float)
with qutip.CoreOptions(auto_real_casting=False):
assert isinstance(qutip.expect(sz, sz), complex)
28 changes: 12 additions & 16 deletions qutip/tests/core/test_qobj.py
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,12 @@ def test_QobjDiagonals():
assert np.all(b == np.diag(data))


def test_diag_type():
assert qutip.sigmaz().diag().dtype == np.float64
assert (1j * qutip.sigmaz()).diag().dtype == np.complex128
with qutip.CoreOptions(auto_real_casting=False):
assert qutip.sigmaz().diag().dtype == np.complex128

def test_QobjEigenEnergies():
"qutip.Qobj eigenenergies"
data = np.eye(5)
Expand Down Expand Up @@ -1123,21 +1129,11 @@ def test_trace():
assert sz.tr() == 0


def test_no_real_attribute(monkeypatch):
"""This tests ensures that trace still works even if the output of a
specialisation does not have the ``real`` attribute. This is the case for
the tensorflow and cupy data layers."""

def mocker_trace_return(oper):
"""
We simply return a string which does not have the `real` attribute.
"""
return "object without .real"

monkeypatch.setattr(_data, "trace", mocker_trace_return)

sz = qutip.sigmaz() # the choice of the matrix does not matter
assert "object without .real" == sz.tr()
def test_no_real_casting():
sz = qutip.sigmaz()
assert isinstance(sz.tr(), float)
with qutip.CoreOptions(auto_real_casting=False):
assert isinstance(sz.tr(), complex)


@pytest.mark.parametrize('inplace', [True, False], ids=['inplace', 'new'])
Expand Down Expand Up @@ -1267,4 +1263,4 @@ def test_data_as():
@pytest.mark.parametrize('dtype', ["CSR", "Dense"])
def test_qobj_dtype(dtype):
obj = qutip.qeye(2, dtype=dtype)
assert obj.dtype == qutip.data.to.parse(dtype)
assert obj.dtype == qutip.data.to.parse(dtype)

0 comments on commit 75944b8

Please sign in to comment.