Repository: qutip/qutip

Issue:
Bug with Bloch and Ipython.
### Bug Description

`Bloch` raises an error when used in jupyter notebook. This seems to be due to the output of `print_figure` in `_repr_svg_` not being bytecode (maybe it was in the past?) it then defaults to `_repr_png_` and renders correctly the bloch sphere.

### Code to Reproduce the Bug

```shell
import qutip
qutip.Bloch()
```


### Code Output

```shell
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
~/.virtualenvs/qutip4/lib/python3.10/site-packages/IPython/core/formatters.py in __call__(self, obj)
    343             method = get_real_method(obj, self.print_method)
    344             if method is not None:
--> 345                 return method()
    346             return None
    347         else:

~/git_repo/qutip/qutip4/qutip/bloch.py in _repr_svg_(self)
    293         from IPython.core.pylabtools import print_figure
    294         self.render()
--> 295         fig_data = print_figure(self.fig, 'svg').decode('utf-8')
    296         plt.close(self.fig)
    297         return fig_data

AttributeError: 'str' object has no attribute 'decode'
```


### Expected Behaviour

The Bloch sphere should be plotted correctly without any Error.

### Your Environment

```shell
QuTiP Version:      5.0.0.dev0+ee51e50
Numpy Version:      1.22.3
Scipy Version:      1.8.1
Cython Version:     None
Matplotlib Version: 3.5.2
Python Version:     3.10.4
Number of CPUs:     8
BLAS Info:          OPENBLAS
OPENMP Installed:   False
INTEL MKL Ext:      False
Platform Info:      Linux (x86_64)
```


### Additional Context

_No response_


Discussion:

Resolving patch:
diff --git a/qutip/bloch.py b/qutip/bloch.py
--- a/qutip/bloch.py
+++ b/qutip/bloch.py
@@ -294,7 +294,7 @@ def _repr_png_(self):
     def _repr_svg_(self):
         from IPython.core.pylabtools import print_figure
         self.render()
-        fig_data = print_figure(self.fig, 'svg').decode('utf-8')
+        fig_data = print_figure(self.fig, 'svg')
         plt.close(self.fig)
         return fig_data
 


Test patch:
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -100,7 +100,7 @@ jobs:
         # rather than in the GitHub Actions file directly, because bash gives us
         # a proper programming language to use.
         run: |
-          QUTIP_TARGET="tests,graphics,semidefinite"
+          QUTIP_TARGET="tests,graphics,semidefinite,ipython"
           if [[ -z "${{ matrix.nocython }}" ]]; then
             QUTIP_TARGET="$QUTIP_TARGET,runtime_compilation"
           fi
diff --git a/qutip/tests/test_bloch.py b/qutip/tests/test_bloch.py
--- a/qutip/tests/test_bloch.py
+++ b/qutip/tests/test_bloch.py
@@ -471,3 +471,10 @@ def test_vector_errors_color_length(self, vectors, colors):
                    "be equivalent to a 1D array with the same "
                    "size as the number of vectors. ")
         assert str(err.value) == err_msg
+
+
+def test_repr_svg():
+    svg = Bloch()._repr_svg_()
+    assert isinstance(svg, str)
+    assert svg.startswith("<?xml")
+    assert svg.endswith("</svg>\n")