Repository: cblearn/cblearn (language: python)

Issue:
Contributor installation instructions broken
The URL in the contributor installation instructions is broken and needs to be changed to: git@github.com:cblearn/cblearn.git

(cc https://github.com/openjournals/joss-reviews/issues/6139)
triplet_formats.py example does not work
I failed to run the triplet_format example. Here is the stacktrace:

```
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[1], line 62
     58 data = [triplets_ordered, (triplets_boolean, answers_boolean),
     59         (triplets_numeric, answers_numeric), triplet_spmatrix]
     60 formats = ["list-order", "list-boolean", "list-count", "tensor-count"]
---> 62 timings = [
     63     (time_convert_triplet(triplets, to_format),
     64      f"{from_format}->{to_format}")
     65     for from_format, triplets in zip(formats, data)
     66     for to_format in formats
     67 ]
     69 for seconds, desc in sorted(timings):
     70     print(f"{seconds * 1000:.2f}ms {desc}")

Cell In[1], line 63, in <listcomp>(.0)
     58 data = [triplets_ordered, (triplets_boolean, answers_boolean),
     59         (triplets_numeric, answers_numeric), triplet_spmatrix]
     60 formats = ["list-order", "list-boolean", "list-count", "tensor-count"]
     62 timings = [
---> 63     (time_convert_triplet(triplets, to_format),
     64      f"{from_format}->{to_format}")
     65     for from_format, triplets in zip(formats, data)
     66     for to_format in formats
     67 ]
     69 for seconds, desc in sorted(timings):
     70     print(f"{seconds * 1000:.2f}ms {desc}")

Cell In[1], line 54, in time_convert_triplet(triplets, to_format)
     52 def time_convert_triplet(triplets, to_format):
     53     time_start = time.process_time()
---> 54     check_query_response(triplets, result_format=to_format)
     55     return (time.process_time() - time_start)

File ~/test/cblearn/cblearn/utils/_validate_data.py:292, in check_query_response(query, response, result_format, standard)
    290     query = check_tensor_query_response(query, (QueryFormat.TENSOR, input_response_format), standard=False)
    291     query, response = query.coords.T, query.data
--> 292 return check_list_query_response(query, response, (output_query_format, output_response_format),
    293                                  standard=standard)

File ~/test/cblearn/cblearn/utils/_validate_data.py:135, in check_list_query_response(query, response, result_format, standard)
    132     raise ValueError(f"Expects result_format list-..., got {result_format}.")
    134 if response_format is ResponseFormat.ORDER:
--> 135     return check_order_list_query_response(query, response)
    136 elif response_format is ResponseFormat.BOOLEAN:
    137     return check_bool_list_query_response(query, response, standard=standard)

File ~/test/cblearn/cblearn/utils/_validate_data.py:96, in check_order_list_query_response(query, response)
     95 def check_order_list_query_response(query, response):
---> 96     query, response = _check_list_query_response(query, response)
     97     __, input_response_format = data_format(query, response)
     99     if input_response_format is ResponseFormat.COUNT:

File ~/test/cblearn/cblearn/utils/_validate_data.py:15, in _check_list_query_response(query, response)
     13 def _check_list_query_response(query, response):
     14     if response is None:
---> 15         return check_array(query, dtype=np.uint32), None
     16     else:
     17         return check_X_y(query, response, dtype=np.uint32)

File ~/test/cblearn/venv/lib/python3.10/site-packages/sklearn/utils/validation.py:904, in check_array(array, accept_sparse, accept_large_sparse, dtype, order, copy, force_all_finite, ensure_2d, allow_nd, ensure_min_samples, ensure_min_features, estimator, input_name)
    899 warnings.simplefilter("error", ComplexWarning)
    900 if dtype is not None and xp.isdtype(dtype, "integral"):
    901     # Conversion float -> int should not contain NaN or
    902     # inf (numpy#14412). We cannot use casting='safe' because
    903     # then conversion float -> int would be disallowed.
--> 904     array = _asarray_with_order(array, order=order, xp=xp)
    905     if xp.isdtype(array.dtype, ("real floating", "complex floating")):
    906         _assert_all_finite(
    907             array,
    908             allow_nan=False,
   (...)
    911             input_name=input_name,
    912         )

File ~/test/cblearn/venv/lib/python3.10/site-packages/sklearn/utils/_array_api.py:380, in _asarray_with_order(array, dtype, order, copy, xp)
    378     array = numpy.array(array, order=order, dtype=dtype)
    379 else:
--> 380     array = numpy.asarray(array, order=order, dtype=dtype)
    382 # At this point array is a NumPy ndarray. We convert it to an array
    383 # container that is consistent with the input's namespace.
    384 return xp.asarray(array)

ValueError: setting an array element with a sequence. The requested array has an inhomogeneous shape after 2 dimensions. The detected shape was (2, 1000000) + inhomogeneous part.
```

Run with Ubuntu 22.10, Python 3.10, based on latest version from repo installed following contributor instructions. 

(cc https://github.com/openjournals/joss-reviews/issues/6139)
triplet_formats.py example does not work
I failed to run the triplet_format example. Here is the stacktrace:

```
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[1], line 62
     58 data = [triplets_ordered, (triplets_boolean, answers_boolean),
     59         (triplets_numeric, answers_numeric), triplet_spmatrix]
     60 formats = ["list-order", "list-boolean", "list-count", "tensor-count"]
---> 62 timings = [
     63     (time_convert_triplet(triplets, to_format),
     64      f"{from_format}->{to_format}")
     65     for from_format, triplets in zip(formats, data)
     66     for to_format in formats
     67 ]
     69 for seconds, desc in sorted(timings):
     70     print(f"{seconds * 1000:.2f}ms {desc}")

Cell In[1], line 63, in <listcomp>(.0)
     58 data = [triplets_ordered, (triplets_boolean, answers_boolean),
     59         (triplets_numeric, answers_numeric), triplet_spmatrix]
     60 formats = ["list-order", "list-boolean", "list-count", "tensor-count"]
     62 timings = [
---> 63     (time_convert_triplet(triplets, to_format),
     64      f"{from_format}->{to_format}")
     65     for from_format, triplets in zip(formats, data)
     66     for to_format in formats
     67 ]
     69 for seconds, desc in sorted(timings):
     70     print(f"{seconds * 1000:.2f}ms {desc}")

Cell In[1], line 54, in time_convert_triplet(triplets, to_format)
     52 def time_convert_triplet(triplets, to_format):
     53     time_start = time.process_time()
---> 54     check_query_response(triplets, result_format=to_format)
     55     return (time.process_time() - time_start)

File ~/test/cblearn/cblearn/utils/_validate_data.py:292, in check_query_response(query, response, result_format, standard)
    290     query = check_tensor_query_response(query, (QueryFormat.TENSOR, input_response_format), standard=False)
    291     query, response = query.coords.T, query.data
--> 292 return check_list_query_response(query, response, (output_query_format, output_response_format),
    293                                  standard=standard)

File ~/test/cblearn/cblearn/utils/_validate_data.py:135, in check_list_query_response(query, response, result_format, standard)
    132     raise ValueError(f"Expects result_format list-..., got {result_format}.")
    134 if response_format is ResponseFormat.ORDER:
--> 135     return check_order_list_query_response(query, response)
    136 elif response_format is ResponseFormat.BOOLEAN:
    137     return check_bool_list_query_response(query, response, standard=standard)

File ~/test/cblearn/cblearn/utils/_validate_data.py:96, in check_order_list_query_response(query, response)
     95 def check_order_list_query_response(query, response):
---> 96     query, response = _check_list_query_response(query, response)
     97     __, input_response_format = data_format(query, response)
     99     if input_response_format is ResponseFormat.COUNT:

File ~/test/cblearn/cblearn/utils/_validate_data.py:15, in _check_list_query_response(query, response)
     13 def _check_list_query_response(query, response):
     14     if response is None:
---> 15         return check_array(query, dtype=np.uint32), None
     16     else:
     17         return check_X_y(query, response, dtype=np.uint32)

File ~/test/cblearn/venv/lib/python3.10/site-packages/sklearn/utils/validation.py:904, in check_array(array, accept_sparse, accept_large_sparse, dtype, order, copy, force_all_finite, ensure_2d, allow_nd, ensure_min_samples, ensure_min_features, estimator, input_name)
    899 warnings.simplefilter("error", ComplexWarning)
    900 if dtype is not None and xp.isdtype(dtype, "integral"):
    901     # Conversion float -> int should not contain NaN or
    902     # inf (numpy#14412). We cannot use casting='safe' because
    903     # then conversion float -> int would be disallowed.
--> 904     array = _asarray_with_order(array, order=order, xp=xp)
    905     if xp.isdtype(array.dtype, ("real floating", "complex floating")):
    906         _assert_all_finite(
    907             array,
    908             allow_nan=False,
   (...)
    911             input_name=input_name,
    912         )

File ~/test/cblearn/venv/lib/python3.10/site-packages/sklearn/utils/_array_api.py:380, in _asarray_with_order(array, dtype, order, copy, xp)
    378     array = numpy.array(array, order=order, dtype=dtype)
    379 else:
--> 380     array = numpy.asarray(array, order=order, dtype=dtype)
    382 # At this point array is a NumPy ndarray. We convert it to an array
    383 # container that is consistent with the input's namespace.
    384 return xp.asarray(array)

ValueError: setting an array element with a sequence. The requested array has an inhomogeneous shape after 2 dimensions. The detected shape was (2, 1000000) + inhomogeneous part.
```

Run with Ubuntu 22.10, Python 3.10, based on latest version from repo installed following contributor instructions. 

(cc https://github.com/openjournals/joss-reviews/issues/6139)
r_wrapper installation instructions missing
The ordinal embedding example cannot be executed without the r_wrapper. The installation for this is not documented and does not work out-of-the-box on a clean Ubuntu 22.04. 

In general, there should be transparency for which features the r-wrapper is required, as well as further information what needs to be installed prior to calling `pip install cblearn[r_wrapper]`. 

(cc https://github.com/openjournals/joss-reviews/issues/6139)


Discussion / hints:
I was going to mention the same as a minor issue. Thanks for bringing that up. 
(cc https://github.com/openjournals/joss-reviews/issues/6139)
I get the same error with python 3.9 and Ubuntu 22.04.3. 

(cc https://github.com/openjournals/joss-reviews/issues/6139)
I get the same error with python 3.9 and Ubuntu 22.04.3. 

(cc https://github.com/openjournals/joss-reviews/issues/6139)


Fix patch:
diff --git a/cblearn/datasets/_car_similarity.py b/cblearn/datasets/_car_similarity.py
index fd34582..a17698e 100644
--- a/cblearn/datasets/_car_similarity.py
+++ b/cblearn/datasets/_car_similarity.py
@@ -38,8 +38,8 @@ def fetch_car_similarity(data_home: Optional[os.PathLike] = None, download_if_mi
     ['OFF-ROAD / SPORT UTILITY VEHICLES', 'ORDINARY CARS', 'OUTLIERS', 'SPORTS CARS']
     >>> dataset.triplet.shape  # doctest: +REMOTE_DATA
     (7097, 3)
-    >>> rounds, round_count = np.unique(dataset.survey_round, return_counts=True)
-    >>> len(rounds), round_count.min(), round_count.max()
+    >>> rounds, round_count = np.unique(dataset.survey_round, return_counts=True)  # doctest: +REMOTE_DATA
+    >>> len(rounds), round_count.min(), round_count.max()  # doctest: +REMOTE_DATA
     (146, 30, 50)
 
     Args:
diff --git a/cblearn/datasets/_imagenet_similarity.py b/cblearn/datasets/_imagenet_similarity.py
index 2e3cac6..bf18a74 100644
--- a/cblearn/datasets/_imagenet_similarity.py
+++ b/cblearn/datasets/_imagenet_similarity.py
@@ -110,7 +110,13 @@ def fetch_imagenet_similarity(data_home: Optional[os.PathLike] = None, download_
         urlretrieve(ARCHIVE.url, archive_path)
 
         with zipfile.ZipFile(archive_path) as zf:
-            import h5py
+            try:
+                import h5py
+            except ImportError:
+                raise ImportError(
+                    "This function needs the extra package 'h5py' but could not find it.\n"
+                    "The package can be installed with pip install h5py.\n"
+                    "On some platforms you might have to install hdf5 libraries separately.")
 
             with zf.open('data/deprecated/psiz0.4.1/obs-118.hdf5', 'r') as f:
                 data_v1 = {k: np.asarray(v[()]) for k, v in h5py.File(f, mode='r').items()}
diff --git a/cblearn/embedding/_dims.py b/cblearn/embedding/_dims.py
index a4ea600..05f6d1d 100644
--- a/cblearn/embedding/_dims.py
+++ b/cblearn/embedding/_dims.py
@@ -144,6 +144,7 @@ def estimate_dimensionality_cv(estimator, queries, responses=None,
           result: A result object with the estimated dimension and other information.
 
         Examples:
+
         >>> from cblearn.embedding import estimate_dimensionality_cv
         >>> from cblearn.embedding import SOE
         >>> from cblearn.datasets import make_random_triplets
@@ -154,8 +155,8 @@ def estimate_dimensionality_cv(estimator, queries, responses=None,
         >>> dim_result = estimate_dimensionality_cv(estimator, triplets, test_dimensions=[1, 2, 3], n_splits=5, refit=True)
         >>> dim_result.estimated_dimension
         2
-        >>> estimator.embedding_.shape
-        (15, 2)
+        >>> true_embedding.shape == estimator.embedding_.shape
+        True
         >>> dim_result.plot_scores()
 
 
diff --git a/cblearn/embedding/wrapper/_r_base.py b/cblearn/embedding/wrapper/_r_base.py
index 3b177e1..3f7e8de 100644
--- a/cblearn/embedding/wrapper/_r_base.py
+++ b/cblearn/embedding/wrapper/_r_base.py
@@ -17,8 +17,8 @@ def init_r(cls):
             numpy2ri.activate()
         except ImportError:
             raise ImportError("Expects installed python package 'rpy2', could not find it. "
-                              "Did you install cblearn with the r_wrapper extras? "
-                              "pip install cblearn[r_wrapper]")
+                              "Did you install cblearn with the wrapper extras? "
+                              "pip install cblearn[wrapper]")
 
     @classmethod
     def import_r_package(cls, package_name, install_if_missing=True, **kwargs):
diff --git a/docs/conf.py b/docs/conf.py
index 5618309..1af713d 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -49,7 +49,7 @@
 
 sphinx_gallery_conf = {
      'examples_dirs': '../examples',   # path to your example scripts
-     #'filename_pattern': r'.\.py',
+     'filename_pattern': r'.*\.py',  # run all .py files in the directory
      'gallery_dirs': 'generated_examples',  # path to where to save gallery generated output
 }
 
diff --git a/docs/contributor_guide/index.rst b/docs/contributor_guide/index.rst
index e3e233d..9fdd2bd 100644
--- a/docs/contributor_guide/index.rst
+++ b/docs/contributor_guide/index.rst
@@ -27,7 +27,6 @@ a `tests` folder with unit tests.
 There should be such a test for every method and function.
 Use ``pytest --cov`` to run these tests and to measure the coverage; no tests should fail.
 The coverage indicates the tested fraction of code and should be close to *100%*.
-You can exclude some of the more time expensive tests by ``pytest -m "not (sklearn or download)``.
 
 All Python code follows the `PEP8 Style Guide`_. The style
 of all code can be checked, running ``flake8 .`` and should print no warnings.
@@ -44,6 +43,25 @@ Typechecks can be performed using ``mypy cblearn``.
 .. _Google Docstring Style: https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html
 .. _type hints: https://docs.python.org/3/library/typing.html
 
+Remote data tests
+-----------------
+Tests that require remote data, for example fetching a dataset from the internet, are marked with ``@pytest.mark.remote_data``
+or ``+REMOTE_DATA`` (docstring).
+These tests are skipped by default but can be run by adding the ``--remote-data`` flag to ``pytest``.
+
+Scikit-learn estimator tests
+----------------------------
+``scikit-learn`` provides a test suite that should ensure the compatibility of estimators.
+We use this test suite to test our estimators, too, but have to skip some tests because they use artificial data incompatible
+to comparison data. Typically, ``cblearn`` estimators are compatible with ``scikit-learn`` estimators
+if comparisons are represented as ``numpy`` arrays. From an API perspective,
+comparison arrays look like discrete features and class labels; however, not all discrete features and class labels are valid comparisons.
+
+In the future scikit-learn might simplify the usage of custom data generation routines during the compatibility tests.
+Otherwise, we might replace those incompatible tests with our own.
+
+All sklearn estimator tests can be skipped with ``pytest -m "not sklearn``.
+
 ----------------------
 Changing Documentation
 ----------------------
@@ -96,7 +114,7 @@ Usually, after some iterations, your changes will be merged into the ``main`` br
 
 
 Versions should be semantic and follow PIP440_: The version indicates ``major.minor.fix``;
-breaking changes are just allowed with major version steps. 
+breaking changes are just allowed with major version steps.
 A Github release tag indicates a new version, which triggers a continuous deployment to PyPI via Github Actions.
 
 .. _PIP440: https://peps.python.org/pep-0440/
diff --git a/docs/install.rst b/docs/install.rst
index 5a70712..8337293 100644
--- a/docs/install.rst
+++ b/docs/install.rst
@@ -19,38 +19,46 @@ The package is mainly tested on Linux, but Windows and Mac OS should work, too.
 User Installation
 -----------------
 
-``cblearn`` and its dependencies can be installed using `pip`:
+``cblearn`` and can be installed using `pip`:
 
 .. code-block:: bash
 
     pip install cblearn
 
+This will install the minimal set of required packages, sufficient for most uses and saving disk space.
+However, some features require more packages that can be installed by adding an ``option`` to the install command.
 
 .. _extras_install:
 
-Extras Requirements
+Extra Requirements
 ===================
 
-The installation routine above installs a minimal set of required packages, sufficient
-for most uses.
-However, some features require more packages that can be installed by adding 
-an `option` to the install command..
-
-For example, to use estimators on GPU, based on ``pytorch``, and estimators
-wrapping paper author's original implementation in ``R``-lang:
+Extra requirements can be installed by adding an ``option`` to the install command and enable more advanced features.
+Some of those extra dependencies need non-Python packages to be installed first.
 
 .. code-block:: bash
 
-    $ pip install cblearn[torch,wrapper]
+    $ pip install cblearn[torch,wrapper] h5py
+
+
+torch
+    Most estimators provide an (optional) implementation using ``pytorch`` to run large datasets on CPU and GPU.
+    This requires the ``pytorch`` package to be installed `manually <https://pytorch.org/get-started/locally/>`_
+    or by adding the ``torch``` option to the install command.
+    Note that ``pytorch`` might need about 1GB of disk space.
+
+wrapper
+    The estimators in :ref:`references_embedding_wrapper` provide an Python interface to the original implementation
+    in ``R``-lang.
+    This requires the ``rpy2`` package to be installed by adding the ``wrapper`` option to the install command.
+    Additionally, this requires an installed ``R`` interpreter whit must available be in the ``PATH`` environment variable.
+    The ``R`` packages are installed automatically upon the first use of the estimators.
 
-======= =============================================================
-Extras  Description
-======= =============================================================
-torch   Estimators which can run on a GPU, implemented using pytorch.
-wrapper Estimators which wrap implementations in R or Matlab/Octave.
-tests   Test runner for unit tests (required to contribute).
-docs    Build the documentation (required to contribute).
-======= =============================================================
+h5py
+    The function :func:`cblearn.datasets.fetch_imagenet_similarity` requires the ``h5py`` package to load the dataset.
+    This can package can be installed with pip.
+    Note that some platforms require additionally the ``hdf5`` libraries to be installed
+     `manually <https://www.hdfgroup.org/downloads/hdf5/>`_.
 
 
 .. _developer_install:
@@ -59,13 +67,33 @@ docs    Build the documentation (required to contribute).
 Contributor Installation
 ------------------------
 
+
 If you want to make changes to the code or documentation, you should
 first download the repository and install the project in developer mode with
-developer dependencies.
-This way, changes in the code are directly considered without the need for re-installation.
+developer dependencies.  This way, changes in the code are directly considered without the need for re-installation.
+Additionally, packages required to run the tests and build the documentation are installed.
 
 .. code-block:: bash
 
-    $ git clone git@github.com/cblearn/cblearn.git
+    $ git clone git@github.com:cblearn/cblearn.git
     $ cd cblearn
-    $ pip install -e.[tests,docs]
+    $ pip install -e.[tests,docs,torch,wrapper]
+
+The ``-e`` option installs the package in developer mode such that changes in the code are considered directly without re-installation.
+
+tests
+    To run the unit tests, the ``pytest`` package is required, which
+    can be installed by adding the ``tests`` option to the install command.
+
+docs
+    Building these docs requires the ``sphinx`` package, which can be installed by adding the `docs` option to the install command.
+
+
+Now you can run the tests and build the documentation:
+
+.. code-block:: bash
+
+    $ python -m pytest --remote-data  # should run all tests; this can take a while.
+
+    $ cd docs
+    $ make html  # should generate docs/_build/html/index.html
\ No newline at end of file
diff --git a/docs/references/index.rst b/docs/references/index.rst
index b4cc369..cb691b5 100644
--- a/docs/references/index.rst
+++ b/docs/references/index.rst
@@ -79,7 +79,7 @@ Low-level Dataset Utility
 Utility
 -------
 
-.. automodule:: cblearn.embedding
+.. currentmodule:: cblearn.embedding
 
 .. autosummary::
     :toctree: generated/
@@ -88,6 +88,7 @@ Utility
     DimensionEstimationResult
 
 
+.. _references_embedding_wrapper:
 Wrapper
 -------
 
@@ -143,7 +144,7 @@ Wrapper
     preprocessing.triplets_from_multiselect
     preprocessing.triplets_from_oddoneout
     preprocessing.triplets_from_mostcentral
-    preprocessing.SharedLabelEncoder
+    preprocessing.SharedColumnEncoder
     preprocessing.MultiColumnLabelEncoder
 
 :mod:`cblearn.utils` Utility
diff --git a/docs/user_guide/index.rst b/docs/user_guide/index.rst
index 87ab13b..8deaf1f 100644
--- a/docs/user_guide/index.rst
+++ b/docs/user_guide/index.rst
@@ -21,10 +21,43 @@ inequality:
     \delta(x_i, x_j) \le \delta(x_i, x_k).
 
 This library supports two representation formats of triplets in an array or a sparse matrix form.
-The array form uses 2d ``numpy`` arrays representing a triplet per row and columns for ``i,j,k``. 
+The array form uses 2d ``numpy`` arrays representing a triplet per row and columns for ``i,j,k``.
 Alternatively to the ordering, an additional response array containing 1 or -1 can specify if  ``(i,j,k)`` is correct or wrong.
 The sparse matrix is an alternative representation, where triplets are naturally specified as the matrix indices, containing entries 1 or -1.
 
+--------------------------
+Scikit-learn compatibility
+--------------------------
+
+All estimators in this library are compatible with the ``scikit-learn`` API and can be used in ``scikit-learn`` pipelines
+if comparisons are represented in the array format.
+The ``scikit-learn`` compatibility is achieved by implementing the ``fit``, ``predict``, and ``score`` methods of the ``BaseEstimator`` class.
+
+The ``fit`` method is used to train the model, the ``predict`` method is used to predict the labels of the test data,
+and the ``score`` method is used to evaluate the model on the test data.
+In the case of ordinal embedding, for example, the ``predict`` method returns the triplet response according to the embedding
+and the ``score`` method returns the triplet accuracy (the fraction of correct triplet responses).
+
+The :ref:`example_ordinal_embedding` example shows how to use a scikit-learn cross validation function with an ordinal embedding estimator.
+
+-------------------------
+Pytorch backend (CPU/GPU)
+-------------------------
+
+The default backend for computations is the ``scipy`` stack, optimized for fast CPU computations and minimal overhead in both compute and disk space.
+However, this comes with limitations when implementing new methods and for calculations with very large data sets.
+
+As an alternative for some estimators, a ``pytorch`` implementation exists.
+ To use this implementation, ``pytorch`` must be installed (see :ref:`extras_install`) and, if necessary,
+ the option ``backend='torch'`` must be set (see the respective function documentation).
+These estimators take care automatically of the data transfer between numpy and torch (internal data representation) and
+use a batched optimizer for faster convergence. If a CUDA GPU is available, the computations are automatically performed on the GPU.
+
+``pytorch`` itself needs a lot of hard disk space and starting the optimization has a certain overhead
+(automatic derivation, data transformation).
+ It is therefore advisable to use the ``scipy`` backend by default and only change if necessary.
+
+
 
 -------------------------
 Dataset loading utilities
diff --git a/examples/ordinal_embedding.py b/examples/ordinal_embedding.py
index f37cdc0..9e27155 100644
--- a/examples/ordinal_embedding.py
+++ b/examples/ordinal_embedding.py
@@ -1,13 +1,16 @@
 # -*- coding: utf-8 -*-
 r"""
+.. _example_ordinal_embedding:
 Ordinal Embedding
 =================
 
 Ordinal embedding algorithms are used to estimate d-dimensional Euclidean coordinates from comparison data.
 
-So in this example, we first generate some triplet comparisons from Euclidean points.
-In the usual applications of ordinal embedding algorithms, these true points cannot be determined and are unknown.
-We try to reconstruct the original points from the comparisons and quantify how good this works.
+In this example, we first generate some triplet comparisons from Euclidean points.
+If you want to use triplets from a human experiment, these ground-truth points are actually unknown.
+
+We try to reconstruct the points from the comparisons and quantify. As we know the ground-truth points here,
+we can evaluate how good this works.
 """
 from sklearn.datasets import make_blobs
 from cblearn.datasets import make_random_triplets
@@ -23,7 +26,7 @@
 # %%
 # The ordinal embedding estimators in cblearn follow the interface of scikit-learn's transformers.
 # Let's estimate coordinates in a 2-dimensional and in a 3-dimensional Euclidean space.
-from cblearn.embedding.wrapper import SOE  # noqa: E402 linter ignore import not at top of file
+from cblearn.embedding import SOE  # noqa: E402 linter ignore import not at top of file
 
 
 transformer_2d = SOE(n_components=2)
diff --git a/examples/small_embedding_benchmark.py b/examples/small_embedding_benchmark.py
index 548ce7a..dfd31fd 100644
--- a/examples/small_embedding_benchmark.py
+++ b/examples/small_embedding_benchmark.py
@@ -37,8 +37,8 @@ def benchmark_embedding(embedding, n_dims):
     print(f"{estimator}: test  acc {np.mean(test_accs):.2f} (sd: {np.std(test_accs):.2f})")
 
 
-n_samples = 100
-n_triplets = 1_000
+n_samples, n_triplets = 10, 100
+#  n_samples, n_triplets = 100, 1_000   # uncomment to use 10x more data
 n_repeat = 10
 estimator = embedding.SOE(1, n_init=1)
 
@@ -52,4 +52,3 @@ def benchmark_embedding(embedding, n_dims):
 benchmark_embedding(estimator, n_dims=1)
 benchmark_embedding(estimator, n_dims=3)
 benchmark_embedding(estimator, n_dims=10)
-
diff --git a/examples/triplet_formats.py b/examples/triplet_formats.py
index 74f797f..d097962 100644
--- a/examples/triplet_formats.py
+++ b/examples/triplet_formats.py
@@ -51,7 +51,11 @@
 
 def time_convert_triplet(triplets, to_format):
     time_start = time.process_time()
-    check_query_response(triplets, result_format=to_format)
+    if len(triplets) == 2:
+        triplets, answers = triplets
+        check_query_response(triplets, answers, result_format=to_format)
+    else:
+        check_query_response(triplets, result_format=to_format)
     return (time.process_time() - time_start)
 
 
diff --git a/setup.cfg b/setup.cfg
index 4b36ffc..2a4095c 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -31,11 +31,11 @@ packages = find:
 zip_safe = False
 include_package_data = True
 install_requires =
-    numpy~=1.24  # numba 0.57 requires numpy <1.25
+    numpy~=1.26  # numba 0.57 requires numpy <1.25
     scipy~=1.11
     scikit-learn~=1.3
-    sparse~=0.14
-    numba>=0.57  # force numba version supporting py3.11
+    sparse~=0.15
+    numba>=0.58  # force numba version supporting py3.11
 
 
 [options.package_data]
@@ -43,27 +43,26 @@ install_requires =
 
 [options.extras_require]
 torch =
-    torch~=2.0.0
+    torch~=2.1
 r_wrapper =
     rpy2~=3.5
 octave_wrapper =
     oct2py~=5.6
 tests =
-    pytest~=7.2
-    pytest-cov~=4.0
-    pytest-doctestplus~=0.12
+    pytest~=7.4
+    pytest-cov~=4.1
+    pytest-doctestplus~=1.1
     pytest-remotedata~=0.4
-    flake8~=6.0
-    mypy~=1.1
-    pandas~=1.5
+    flake8~=7.0
+    mypy~=1.8
+    pandas~=2.1
     matplotlib~=3.8
 docs =
-    sphinx~=6.1
-    sphinx_rtd_theme~=1.2
-    sphinx-gallery~=0.12
+    sphinx~=7.2
+    sphinx_rtd_theme~=2.0
+    sphinx-gallery~=0.15
     matplotlib~=3.8
 
-
 [build_sphinx]
 all-files = 1
 source-dir = docs/


Test patch:
diff --git a/cblearn/utils/tests/test_validate_data.py b/cblearn/utils/tests/test_validate_data.py
index 4e5aa3b..4417355 100644
--- a/cblearn/utils/tests/test_validate_data.py
+++ b/cblearn/utils/tests/test_validate_data.py
@@ -35,7 +35,7 @@ def test_check_query():
 
     triplets = utils.check_query(triplets_ordered, result_format='tensor')
     assert (triplets == utils.check_query(triplets_spmatrix)).all()
-    np.testing.assert_equal(np.triu(triplets), triplets)
+    np.testing.assert_equal(np.triu(triplets).todense(), triplets.todense())
 
     triplets = utils.check_query(triplets_spmatrix, result_format='list')
     np.testing.assert_equal(triplets, triplets_explicit)