Repository: qutip/qutip

Issue:
dev.major's Qobj's __matmul__ set wrong superrep for operator-ket
**Describe the bug**
`operator-ket`'s `superrep` is `None` at creation.
But `Qobj.__matmul__` set the `superrep` of the output to the one of the left `Qobj`.
Thus `super-operator @ operator-ket` result  in an `operator-ket` with `superrep != None`.
Some operations, such as addition, cannot be done between object with different `superrep`.

**To Reproduce**
```
operket = qutip.operator_to_vector(qutip.qeye(2))
super_oper = qutip.spre(qutip.qeye(2))
super_oper @ operket - operket
```
the last operation result in a `TypeError`.

(#1476)


Discussion:

Resolving patch:
diff --git a/qutip/core/qobj.py b/qutip/core/qobj.py
--- a/qutip/core/qobj.py
+++ b/qutip/core/qobj.py
@@ -377,7 +377,7 @@ def __init__(self, arg=None, dims=None, type=None,
                     repr(self._data.shape[0]),
                 ]))
             self.dims = [[[root]]*2]*2
-        if self.type == 'super':
+        if self.type in ['super', 'operator-ket', 'operator-bra']:
             superrep = superrep or 'super'
         self.superrep = superrep
 
@@ -507,7 +507,7 @@ def __matmul__(self, other):
                 " and ",
                 repr(other.dims),
             ]))
-        if self.issuper and other.issuper and self.superrep != other.superrep:
+        if self.superrep != other.superrep:
             raise TypeError("".join([
                 "incompatible superoperator representations ",
                 repr(self.superrep),
diff --git a/qutip/core/superoperator.py b/qutip/core/superoperator.py
--- a/qutip/core/superoperator.py
+++ b/qutip/core/superoperator.py
@@ -266,9 +266,13 @@ def operator_to_vector(op):
     representation.  Note that QuTiP uses the _column-stacking_ convention,
     which may be different to what you expect.
     """
+    if op.type in ['super', 'operator-ket', 'operator-bra']:
+        raise TypeError("Cannot convert object already "
+                        "in super representation")
     return Qobj(stack_columns(op.data),
                 dims=[op.dims, [1]],
                 type='operator-ket',
+                superrep="super",
                 copy=False)
 
 
@@ -280,6 +284,8 @@ def vector_to_operator(op):
     """
     if not op.isoperket:
         raise TypeError("only defined for operator-kets")
+    if op.superrep != "super":
+        raise TypeError("only defined for operator-kets in super format")
     dims = op.dims[0]
     return Qobj(unstack_columns(op.data, (np.prod(dims[0]), np.prod(dims[1]))),
                 dims=dims,


Test patch:
diff --git a/qutip/tests/core/test_qobj.py b/qutip/tests/core/test_qobj.py
--- a/qutip/tests/core/test_qobj.py
+++ b/qutip/tests/core/test_qobj.py
@@ -100,6 +100,19 @@ def test_QobjType():
     super_qobj = qutip.Qobj(super_data, dims=[[[3]], [[3]]])
     assert super_qobj.type == 'super'
     assert super_qobj.issuper
+    assert super_qobj.superrep == 'super'
+
+    super_data = np.random.random(N)
+    super_qobj = qutip.Qobj(super_data, dims=[[[3], [3]], [[1]]])
+    assert super_qobj.type == 'operator-ket'
+    assert super_qobj.isoperket
+    assert super_qobj.superrep == 'super'
+
+    super_data = np.random.random(N)
+    super_qobj = qutip.Qobj(super_data, dims=[[[1]], [[3], [3]]])
+    assert super_qobj.type == 'operator-bra'
+    assert super_qobj.isoperbra
+    assert super_qobj.superrep == 'super'
 
     operket_qobj = qutip.operator_to_vector(oper_qobj)
     assert operket_qobj.isoperket
@@ -387,6 +400,17 @@ def test_CheckMulType():
     assert opbra2.dag() == opket2
 
 
+def test_operator_ket_superrep():
+    sop = qutip.to_super(qutip.sigmax())
+    opket1 = qutip.operator_to_vector(qutip.fock_dm(2, 0))
+    opket2 = sop * opket1
+    assert opket1.superrep == opket2.superrep
+
+    opbra1 = qutip.operator_to_vector(qutip.fock_dm(2, 0)).dag()
+    opbra2 = opbra1 * sop
+    assert opbra1.superrep == opbra2.superrep
+
+
 def test_QobjConjugate():
     "qutip.Qobj conjugate"
     data = _random_not_singular(5)
diff --git a/qutip/tests/core/test_superoper.py b/qutip/tests/core/test_superoper.py
--- a/qutip/tests/core/test_superoper.py
+++ b/qutip/tests/core/test_superoper.py
@@ -36,6 +36,7 @@
 
 import qutip
 from qutip.core import data as _data
+import pytest
 
 
 def f(t, args):
@@ -67,6 +68,25 @@ def testOperatorVector(self):
         rho2 = qutip.vector_to_operator(qutip.operator_to_vector(rho1))
         np.testing.assert_allclose(rho1.full(), rho2.full(), 1e-8)
 
+    def testsuperrep(self):
+        N = 3
+        rho1 = qutip.rand_dm(N)
+        as_vec = qutip.operator_to_vector(rho1)
+        assert as_vec.superrep == 'super'
+
+        with pytest.raises(TypeError) as err:
+            bad_vec = as_vec.copy()
+            bad_vec.superrep = ""
+            qutip.vector_to_operator(bad_vec)
+        assert err.value.args[0] == ("only defined for operator-kets "
+                                     "in super format")
+
+        with pytest.raises(TypeError) as err:
+            qutip.operator_to_vector(as_vec)
+        assert err.value.args[0] == ("Cannot convert object already "
+                                     "in super representation")
+
+
     def testOperatorVectorTensor(self):
         """
         Superoperator: Operator - vector - operator conversion with a tensor product state.