diff --git a/src/main/java/org/mastodon/mamut/feature/branch/dimensionalityreduction/tsne/BranchTSneFeature.java b/src/main/java/org/mastodon/mamut/feature/branch/dimensionalityreduction/tsne/BranchTSneFeature.java
new file mode 100644
index 000000000..3bffc13e3
--- /dev/null
+++ b/src/main/java/org/mastodon/mamut/feature/branch/dimensionalityreduction/tsne/BranchTSneFeature.java
@@ -0,0 +1,85 @@
+/*-
+ * #%L
+ * mastodon-deep-lineage
+ * %%
+ * Copyright (C) 2022 - 2024 Stefan Hahmann
+ * %%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ * #L%
+ */
+package org.mastodon.mamut.feature.branch.dimensionalityreduction.tsne;
+
+import java.util.List;
+
+import org.mastodon.feature.Feature;
+import org.mastodon.feature.FeatureProjectionKey;
+import org.mastodon.feature.FeatureProjectionSpec;
+import org.mastodon.feature.FeatureSpec;
+import org.mastodon.feature.Multiplicity;
+import org.mastodon.mamut.feature.dimensionalityreduction.tsne.feature.AbstractTSneFeature;
+import org.mastodon.mamut.model.branch.BranchSpot;
+import org.mastodon.properties.DoublePropertyMap;
+import org.scijava.plugin.Plugin;
+
+/**
+ * Represents a t-SNE feature for BranchSpots in the Mastodon project.
+ *
+ * This feature is used to store the t-SNE outputs for BranchSpots.
+ *
+ * The t-SNE outputs are stored in a list of {@link DoublePropertyMap}s. The size of the list is equal to the number of dimensions of the t-SNE output.
+ */
+public class BranchTSneFeature extends AbstractTSneFeature< BranchSpot >
+{
+ public static final String KEY = "Branch t-SNE outputs";
+
+ private final BranchSpotTSneFeatureSpec adaptedSpec;
+
+ public static final BranchSpotTSneFeatureSpec GENERIC_SPEC = new BranchSpotTSneFeatureSpec();
+
+ public BranchTSneFeature( final List< DoublePropertyMap< BranchSpot > > outputMaps )
+ {
+ super( outputMaps );
+ FeatureProjectionSpec[] projectionSpecs =
+ projectionMap.keySet().stream().map( FeatureProjectionKey::getSpec ).toArray( FeatureProjectionSpec[]::new );
+ this.adaptedSpec = new BranchSpotTSneFeatureSpec( projectionSpecs );
+ }
+
+ @Plugin( type = FeatureSpec.class )
+ public static class BranchSpotTSneFeatureSpec extends FeatureSpec< BranchTSneFeature, BranchSpot >
+ {
+ public BranchSpotTSneFeatureSpec()
+ {
+ super( KEY, HELP_STRING, BranchTSneFeature.class, BranchSpot.class, Multiplicity.SINGLE );
+ }
+
+ public BranchSpotTSneFeatureSpec( final FeatureProjectionSpec... projectionSpecs )
+ {
+ super( KEY, HELP_STRING, BranchTSneFeature.class, BranchSpot.class, Multiplicity.SINGLE, projectionSpecs );
+ }
+ }
+
+ @Override
+ public FeatureSpec< ? extends Feature< BranchSpot >, BranchSpot > getSpec()
+ {
+ return adaptedSpec;
+ }
+}
diff --git a/src/main/java/org/mastodon/mamut/feature/branch/dimensionalityreduction/tsne/BranchTSneFeatureComputer.java b/src/main/java/org/mastodon/mamut/feature/branch/dimensionalityreduction/tsne/BranchTSneFeatureComputer.java
new file mode 100644
index 000000000..f167f20cb
--- /dev/null
+++ b/src/main/java/org/mastodon/mamut/feature/branch/dimensionalityreduction/tsne/BranchTSneFeatureComputer.java
@@ -0,0 +1,76 @@
+/*-
+ * #%L
+ * mastodon-deep-lineage
+ * %%
+ * Copyright (C) 2022 - 2024 Stefan Hahmann
+ * %%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ * #L%
+ */
+package org.mastodon.mamut.feature.branch.dimensionalityreduction.tsne;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import org.mastodon.RefPool;
+import org.mastodon.mamut.feature.dimensionalityreduction.tsne.feature.AbstractTSneFeature;
+import org.mastodon.mamut.feature.dimensionalityreduction.tsne.feature.AbstractTSneFeatureComputer;
+import org.mastodon.mamut.model.Model;
+import org.mastodon.mamut.model.branch.BranchLink;
+import org.mastodon.mamut.model.branch.BranchSpot;
+import org.mastodon.mamut.model.branch.ModelBranchGraph;
+import org.mastodon.properties.DoublePropertyMap;
+import org.scijava.Context;
+
+public class BranchTSneFeatureComputer extends AbstractTSneFeatureComputer< BranchSpot, BranchLink, ModelBranchGraph >
+{
+
+ public BranchTSneFeatureComputer( final Model model, final Context context )
+ {
+ super( model, context );
+ }
+
+ @Override
+ protected AbstractTSneFeature< BranchSpot > createFeatureInstance( final List< DoublePropertyMap< BranchSpot > > umapOutputMaps )
+ {
+ return new BranchTSneFeature( umapOutputMaps );
+ }
+
+ @Override
+ protected RefPool< BranchSpot > getRefPool()
+ {
+ return model.getBranchGraph().vertices().getRefPool();
+ }
+
+ @Override
+ protected ReentrantReadWriteLock getLock( final ModelBranchGraph branchGraph )
+ {
+ return branchGraph.getLock();
+ }
+
+ @Override
+ protected Collection< BranchSpot > getVertices()
+ {
+ return model.getBranchGraph().vertices();
+ }
+}
diff --git a/src/main/java/org/mastodon/mamut/feature/branch/dimensionalityreduction/tsne/BranchTSneFeatureSerializer.java b/src/main/java/org/mastodon/mamut/feature/branch/dimensionalityreduction/tsne/BranchTSneFeatureSerializer.java
new file mode 100644
index 000000000..8ffae45a1
--- /dev/null
+++ b/src/main/java/org/mastodon/mamut/feature/branch/dimensionalityreduction/tsne/BranchTSneFeatureSerializer.java
@@ -0,0 +1,72 @@
+/*-
+ * #%L
+ * mastodon-deep-lineage
+ * %%
+ * Copyright (C) 2022 - 2024 Stefan Hahmann
+ * %%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ * #L%
+ */
+package org.mastodon.mamut.feature.branch.dimensionalityreduction.tsne;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+import org.mastodon.feature.FeatureSpec;
+import org.mastodon.feature.io.FeatureSerializer;
+import org.mastodon.io.FileIdToObjectMap;
+import org.mastodon.io.ObjectToFileIdMap;
+import org.mastodon.mamut.feature.branch.BranchFeatureSerializer;
+import org.mastodon.mamut.feature.branch.dimensionalityreduction.BranchOutputSerializerTools;
+import org.mastodon.mamut.model.ModelGraph;
+import org.mastodon.mamut.model.Spot;
+import org.mastodon.mamut.model.branch.BranchSpot;
+import org.mastodon.mamut.model.branch.ModelBranchGraph;
+import org.scijava.plugin.Plugin;
+
+/**
+ * De-/serializes {@link BranchTSneFeature}
+ */
+@Plugin( type = FeatureSerializer.class )
+public class BranchTSneFeatureSerializer implements BranchFeatureSerializer< BranchTSneFeature, BranchSpot, Spot >
+{
+ @Override
+ public FeatureSpec< BranchTSneFeature, BranchSpot > getFeatureSpec()
+ {
+ return BranchTSneFeature.GENERIC_SPEC;
+ }
+
+ @Override
+ public void serialize( final BranchTSneFeature feature, final ObjectToFileIdMap< Spot > idmap, final ObjectOutputStream oos,
+ final ModelBranchGraph branchGraph, final ModelGraph graph ) throws IOException
+ {
+ BranchOutputSerializerTools.serialize( feature, idmap, oos, branchGraph, graph );
+ }
+
+ @Override
+ public BranchTSneFeature deserialize( final FileIdToObjectMap< Spot > idmap, final ObjectInputStream ois,
+ final ModelBranchGraph branchGraph, final ModelGraph graph ) throws ClassNotFoundException, IOException
+ {
+ return BranchOutputSerializerTools.deserialize( idmap, ois, branchGraph, graph, BranchTSneFeature::new );
+ }
+}
diff --git a/src/main/java/org/mastodon/mamut/feature/dimensionalityreduction/DimensionalityReductionController.java b/src/main/java/org/mastodon/mamut/feature/dimensionalityreduction/DimensionalityReductionController.java
index 618bd351a..c1bde32f4 100644
--- a/src/main/java/org/mastodon/mamut/feature/dimensionalityreduction/DimensionalityReductionController.java
+++ b/src/main/java/org/mastodon/mamut/feature/dimensionalityreduction/DimensionalityReductionController.java
@@ -37,11 +37,14 @@
import org.mastodon.graph.Edge;
import org.mastodon.graph.ReadOnlyGraph;
import org.mastodon.graph.Vertex;
+import org.mastodon.mamut.feature.branch.dimensionalityreduction.tsne.BranchTSneFeatureComputer;
import org.mastodon.mamut.feature.branch.dimensionalityreduction.umap.BranchUmapFeatureComputer;
import org.mastodon.mamut.feature.dimensionalityreduction.tsne.TSneSettings;
+import org.mastodon.mamut.feature.dimensionalityreduction.tsne.feature.AbstractTSneFeatureComputer;
import org.mastodon.mamut.feature.dimensionalityreduction.umap.UmapSettings;
import org.mastodon.mamut.feature.dimensionalityreduction.umap.feature.AbstractUmapFeatureComputer;
import org.mastodon.mamut.feature.dimensionalityreduction.util.InputDimension;
+import org.mastodon.mamut.feature.spot.dimensionalityreduction.tsne.SpotTSneFeatureComputer;
import org.mastodon.mamut.feature.spot.dimensionalityreduction.umap.SpotUmapFeatureComputer;
import org.mastodon.mamut.model.Link;
import org.mastodon.mamut.model.Model;
@@ -177,10 +180,23 @@ public void setAlgorithm( final DimensionalityReductionAlgorithm algorithm )
throw new IllegalArgumentException( "Number of output dimensions (" + commonSettings.getNumberOfOutputDimensions()
+ ") must be smaller than the number of input features (" + inputDimensions.size() + ")." );
G graph = getGraph( isModelGraph );
- AbstractUmapFeatureComputer< V, E, G > umapFeatureComputer =
- isModelGraph ? Cast.unchecked( new SpotUmapFeatureComputer( model, context ) )
- : Cast.unchecked( new BranchUmapFeatureComputer( model, context ) );
- umapFeatureComputer.computeFeature( commonSettings, umapSettings, inputDimensions, graph );
+ switch ( algorithm )
+ {
+ case UMAP:
+ AbstractUmapFeatureComputer< V, E, G > umapFeatureComputer =
+ isModelGraph ? Cast.unchecked( new SpotUmapFeatureComputer( model, context ) )
+ : Cast.unchecked( new BranchUmapFeatureComputer( model, context ) );
+ umapFeatureComputer.computeFeature( commonSettings, umapSettings, inputDimensions, graph );
+ break;
+ case TSNE:
+ AbstractTSneFeatureComputer< V, E, G > tSneFeatureComputer =
+ isModelGraph ? Cast.unchecked( new SpotTSneFeatureComputer( model, context ) )
+ : Cast.unchecked( new BranchTSneFeatureComputer( model, context ) );
+ tSneFeatureComputer.computeFeature( commonSettings, tSneSettings, inputDimensions, graph );
+ break;
+ default:
+ throw new IllegalArgumentException( "Unknown algorithm: " + algorithm );
+ }
}
private < V extends Vertex< E >, E extends Edge< V >, G extends ReadOnlyGraph< V, ? > > G getGraph( boolean isSpotGraph )
diff --git a/src/main/java/org/mastodon/mamut/feature/dimensionalityreduction/tsne/feature/AbstractTSneFeature.java b/src/main/java/org/mastodon/mamut/feature/dimensionalityreduction/tsne/feature/AbstractTSneFeature.java
new file mode 100644
index 000000000..099cacf01
--- /dev/null
+++ b/src/main/java/org/mastodon/mamut/feature/dimensionalityreduction/tsne/feature/AbstractTSneFeature.java
@@ -0,0 +1,59 @@
+/*-
+ * #%L
+ * mastodon-deep-lineage
+ * %%
+ * Copyright (C) 2022 - 2024 Stefan Hahmann
+ * %%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ * #L%
+ */
+package org.mastodon.mamut.feature.dimensionalityreduction.tsne.feature;
+
+import java.util.List;
+
+import org.mastodon.graph.Vertex;
+import org.mastodon.mamut.feature.dimensionalityreduction.AbstractOutputFeature;
+import org.mastodon.properties.DoublePropertyMap;
+
+/**
+ * This generic feature is used to store the t-SNE outputs.
+ *
+ * The t-SNE outputs are stored in a list of {@link DoublePropertyMap}s. The size of the list is equal to the number of dimensions of the t-SNE output.
+ */
+public abstract class AbstractTSneFeature< V extends Vertex< ? > > extends AbstractOutputFeature< V >
+{
+ private static final String PROJECTION_NAME_TEMPLATE = "tSNE%d";
+
+ protected static final String HELP_STRING =
+ "Computes the t-SNE according to the selected input dimensions, number of target dimensions, the perplexity value and maximum number of iterations.";
+
+ protected AbstractTSneFeature( final List< DoublePropertyMap< V > > umapOutputMaps )
+ {
+ super( umapOutputMaps );
+ }
+
+ @Override
+ protected String getProjectionNameTemplate()
+ {
+ return PROJECTION_NAME_TEMPLATE;
+ }
+}
diff --git a/src/main/java/org/mastodon/mamut/feature/dimensionalityreduction/tsne/feature/AbstractTSneFeatureComputer.java b/src/main/java/org/mastodon/mamut/feature/dimensionalityreduction/tsne/feature/AbstractTSneFeatureComputer.java
new file mode 100644
index 000000000..44f747194
--- /dev/null
+++ b/src/main/java/org/mastodon/mamut/feature/dimensionalityreduction/tsne/feature/AbstractTSneFeatureComputer.java
@@ -0,0 +1,114 @@
+/*-
+ * #%L
+ * mastodon-deep-lineage
+ * %%
+ * Copyright (C) 2022 - 2024 Stefan Hahmann
+ * %%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ * #L%
+ */
+package org.mastodon.mamut.feature.dimensionalityreduction.tsne.feature;
+
+import java.lang.invoke.MethodHandles;
+import java.util.List;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import com.jujutsu.tsne.TSneConfiguration;
+import com.jujutsu.tsne.barneshut.BarnesHutTSne;
+import com.jujutsu.tsne.barneshut.ParallelBHTsne;
+import com.jujutsu.utils.TSneUtils;
+
+import org.mastodon.RefPool;
+import org.mastodon.graph.Edge;
+import org.mastodon.graph.ReadOnlyGraph;
+import org.mastodon.graph.Vertex;
+import org.mastodon.mamut.feature.dimensionalityreduction.AbstractOutputFeatureComputer;
+import org.mastodon.mamut.feature.dimensionalityreduction.CommonSettings;
+import org.mastodon.mamut.feature.dimensionalityreduction.tsne.TSneSettings;
+import org.mastodon.mamut.feature.dimensionalityreduction.util.InputDimension;
+import org.mastodon.mamut.model.Model;
+import org.mastodon.properties.DoublePropertyMap;
+import org.scijava.Context;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Abstract class for computing t-SNE features in the Mastodon project.
+ *
+ * This provides the base implementation for computing t-SNE features on vertices in a read-only graph.
+ * It handles the setup, execution, and caching of t-SNE computations.
+ *
+ * This class connects the t-SNE library to the Mastodon project by providing the necessary data and settings.
+ * It ensures that only valid data rows (i.e. rows where the selected feature projections do not have values, such as {@link Double#NaN} or {@link Double#POSITIVE_INFINITY}) are used for t-SNE computations.
+ *
+ * @param the type of vertex
+ * @param the type of read-only graph
+ */
+public abstract class AbstractTSneFeatureComputer< V extends Vertex< E >, E extends Edge< V >, G extends ReadOnlyGraph< V, E > >
+ extends AbstractOutputFeatureComputer< V, E, G >
+{
+
+ private static final Logger logger = LoggerFactory.getLogger( MethodHandles.lookup().lookupClass() );
+
+ private TSneSettings tSneSettings;
+
+ private double[][] tSneResult;
+
+ protected AbstractTSneFeatureComputer( final Model model, final Context context )
+ {
+ super( model, context );
+ }
+
+ public void computeFeature( final CommonSettings commonSettings, final TSneSettings tSneSettings,
+ final List< InputDimension< V > > inputDimensions, final G graph )
+ {
+ this.tSneSettings = tSneSettings;
+ super.computeFeature( commonSettings, inputDimensions, graph );
+ }
+
+ @Override
+ protected void computeAlgorithm( double[][] dataMatrix )
+ {
+ TSneConfiguration tSneConfig =
+ TSneUtils.buildConfig( dataMatrix, settings.getNumberOfOutputDimensions(), TSneSettings.INITIAL_DIMENSIONS,
+ tSneSettings.getPerplexity(),
+ tSneSettings.getMaxIterations(), TSneSettings.USE_PCA, TSneSettings.THETA, false, true );
+
+ BarnesHutTSne tsne = new ParallelBHTsne();
+ logger.info( "Computing t-SNE. Data matrix has {} rows x {} columns.", dataMatrix.length, dataMatrix[ 0 ].length );
+ tSneResult = tsne.tsne( tSneConfig );
+ logger.info( "Finished computing t-SNE. Results has {} rows x {} columns.", tSneResult.length,
+ tSneResult.length > 0 ? tSneResult[ 0 ].length : 0 );
+ }
+
+ @Override
+ protected double[][] getResult()
+ {
+ return tSneResult;
+ }
+
+ protected abstract AbstractTSneFeature< V > createFeatureInstance( final List< DoublePropertyMap< V > > umapOutputMaps );
+
+ protected abstract RefPool< V > getRefPool();
+
+ protected abstract ReentrantReadWriteLock getLock( final G graph );
+}
diff --git a/src/main/java/org/mastodon/mamut/feature/spot/dimensionalityreduction/tsne/SpotTSneFeature.java b/src/main/java/org/mastodon/mamut/feature/spot/dimensionalityreduction/tsne/SpotTSneFeature.java
new file mode 100644
index 000000000..5293f76a0
--- /dev/null
+++ b/src/main/java/org/mastodon/mamut/feature/spot/dimensionalityreduction/tsne/SpotTSneFeature.java
@@ -0,0 +1,85 @@
+/*-
+ * #%L
+ * mastodon-deep-lineage
+ * %%
+ * Copyright (C) 2022 - 2024 Stefan Hahmann
+ * %%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ * #L%
+ */
+package org.mastodon.mamut.feature.spot.dimensionalityreduction.tsne;
+
+import java.util.List;
+
+import org.mastodon.feature.Feature;
+import org.mastodon.feature.FeatureProjectionKey;
+import org.mastodon.feature.FeatureProjectionSpec;
+import org.mastodon.feature.FeatureSpec;
+import org.mastodon.feature.Multiplicity;
+import org.mastodon.mamut.feature.dimensionalityreduction.tsne.feature.AbstractTSneFeature;
+import org.mastodon.mamut.model.Spot;
+import org.mastodon.properties.DoublePropertyMap;
+import org.scijava.plugin.Plugin;
+
+/**
+ * Represents a t-SNE feature for spots in the Mastodon project.
+ *
+ * This feature is used to store the t-SNE outputs for spots.
+ *
+ * The t-SNE outputs are stored in a list of {@link DoublePropertyMap}s. The size of the list is equal to the number of dimensions of the t-SNE output.
+ */
+public class SpotTSneFeature extends AbstractTSneFeature< Spot >
+{
+ public static final String KEY = "Spot t-SNE outputs";
+
+ private final SpotTSneFeatureSpec adaptedSpec;
+
+ public static final SpotTSneFeatureSpec GENERIC_SPEC = new SpotTSneFeatureSpec();
+
+ public SpotTSneFeature( final List< DoublePropertyMap< Spot > > outputMaps )
+ {
+ super( outputMaps );
+ FeatureProjectionSpec[] projectionSpecs =
+ projectionMap.keySet().stream().map( FeatureProjectionKey::getSpec ).toArray( FeatureProjectionSpec[]::new );
+ this.adaptedSpec = new SpotTSneFeatureSpec( projectionSpecs );
+ }
+
+ @Plugin( type = FeatureSpec.class )
+ public static class SpotTSneFeatureSpec extends FeatureSpec< SpotTSneFeature, Spot >
+ {
+ public SpotTSneFeatureSpec()
+ {
+ super( KEY, HELP_STRING, SpotTSneFeature.class, Spot.class, Multiplicity.SINGLE );
+ }
+
+ public SpotTSneFeatureSpec( final FeatureProjectionSpec... projectionSpecs )
+ {
+ super( KEY, HELP_STRING, SpotTSneFeature.class, Spot.class, Multiplicity.SINGLE, projectionSpecs );
+ }
+ }
+
+ @Override
+ public FeatureSpec< ? extends Feature< Spot >, Spot > getSpec()
+ {
+ return adaptedSpec;
+ }
+}
diff --git a/src/main/java/org/mastodon/mamut/feature/spot/dimensionalityreduction/tsne/SpotTSneFeatureComputer.java b/src/main/java/org/mastodon/mamut/feature/spot/dimensionalityreduction/tsne/SpotTSneFeatureComputer.java
new file mode 100644
index 000000000..aad5cf950
--- /dev/null
+++ b/src/main/java/org/mastodon/mamut/feature/spot/dimensionalityreduction/tsne/SpotTSneFeatureComputer.java
@@ -0,0 +1,76 @@
+/*-
+ * #%L
+ * mastodon-deep-lineage
+ * %%
+ * Copyright (C) 2022 - 2024 Stefan Hahmann
+ * %%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ * #L%
+ */
+package org.mastodon.mamut.feature.spot.dimensionalityreduction.tsne;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import org.mastodon.RefPool;
+import org.mastodon.mamut.feature.dimensionalityreduction.tsne.feature.AbstractTSneFeature;
+import org.mastodon.mamut.feature.dimensionalityreduction.tsne.feature.AbstractTSneFeatureComputer;
+import org.mastodon.mamut.model.Link;
+import org.mastodon.mamut.model.Model;
+import org.mastodon.mamut.model.ModelGraph;
+import org.mastodon.mamut.model.Spot;
+import org.mastodon.properties.DoublePropertyMap;
+import org.scijava.Context;
+
+public class SpotTSneFeatureComputer extends AbstractTSneFeatureComputer< Spot, Link, ModelGraph >
+{
+
+ public SpotTSneFeatureComputer( final Model model, final Context context )
+ {
+ super( model, context );
+ }
+
+ @Override
+ protected AbstractTSneFeature< Spot > createFeatureInstance( final List< DoublePropertyMap< Spot > > outputMaps )
+ {
+ return new SpotTSneFeature( outputMaps );
+ }
+
+ @Override
+ protected RefPool< Spot > getRefPool()
+ {
+ return model.getGraph().vertices().getRefPool();
+ }
+
+ @Override
+ protected ReentrantReadWriteLock getLock( final ModelGraph graph )
+ {
+ return graph.getLock();
+ }
+
+ @Override
+ protected Collection< Spot > getVertices()
+ {
+ return model.getGraph().vertices();
+ }
+}
diff --git a/src/main/java/org/mastodon/mamut/feature/spot/dimensionalityreduction/tsne/SpotTSneFeatureSerializer.java b/src/main/java/org/mastodon/mamut/feature/spot/dimensionalityreduction/tsne/SpotTSneFeatureSerializer.java
new file mode 100644
index 000000000..7fc696396
--- /dev/null
+++ b/src/main/java/org/mastodon/mamut/feature/spot/dimensionalityreduction/tsne/SpotTSneFeatureSerializer.java
@@ -0,0 +1,70 @@
+/*-
+ * #%L
+ * mastodon-deep-lineage
+ * %%
+ * Copyright (C) 2022 - 2024 Stefan Hahmann
+ * %%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ * #L%
+ */
+package org.mastodon.mamut.feature.spot.dimensionalityreduction.tsne;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+import org.mastodon.collection.RefCollection;
+import org.mastodon.feature.FeatureSpec;
+import org.mastodon.feature.io.FeatureSerializer;
+import org.mastodon.io.FileIdToObjectMap;
+import org.mastodon.io.ObjectToFileIdMap;
+import org.mastodon.mamut.feature.spot.dimensionalityreduction.SpotOutputFeatureSerializerTools;
+import org.mastodon.mamut.model.Spot;
+import org.scijava.plugin.Plugin;
+
+/**
+ * De-/serializes {@link SpotTSneFeature}
+ */
+@Plugin( type = FeatureSerializer.class )
+public class SpotTSneFeatureSerializer implements FeatureSerializer< SpotTSneFeature, Spot >
+{
+
+ @Override
+ public FeatureSpec< SpotTSneFeature, Spot > getFeatureSpec()
+ {
+ return SpotTSneFeature.GENERIC_SPEC;
+ }
+
+ @Override
+ public void serialize( final SpotTSneFeature feature, final ObjectToFileIdMap< Spot > idMap, final ObjectOutputStream oos )
+ throws IOException
+ {
+ SpotOutputFeatureSerializerTools.serialize( feature, idMap, oos );
+ }
+
+ @Override
+ public SpotTSneFeature deserialize( final FileIdToObjectMap< Spot > idMap, final RefCollection< Spot > pool,
+ final ObjectInputStream ois ) throws IOException, ClassNotFoundException
+ {
+ return SpotOutputFeatureSerializerTools.deserialize( idMap, pool, ois, SpotTSneFeature::new );
+ }
+}