Repository: MICA-MNI/BrainSpace (language: python)

Issue:
[BUG] Failure in validation of graph edge properties
Some users are running into a bug in the graph function when running the tutorial scripts, however I have been unable to reproduce this bug. 

For now I advise users with this bug to comment out the lines shown below of the GradientMaps script. Note that after commenting out these lines the toolbox will no longer check whether the affinity matrix is a connected graph. With resting-state data and default sparsity values it is unlikely for the affinity matrix to represent a disconnected graph, but be aware that this may be an issue with other types of data. 

In a future update I'll remove the reliance on the graph function. 

Comment out these lines:
```
if ~all(conncomp(graph(abs(data),'lower')) == 1) 
    error('Graph is not connected.')
end

```

Error log.
```
gm = GradientMaps();
gm = gm.fit(conn_matrix);
Launching BrainSpace, the gradient connectivity toolbox.
Set the kernel to: Normalized Angle.
Set the approach to: Diffusion Embedding.
Set the alignment to: None.
No random state initialization set.
Set the number of requested components to: 10.
 
Running gradient analysis...
Kernel: Normalized Angle
Approach: Diffusion Embedding
Alignment: None
Running with sparsity parameter: 90
Error using graph.validateEdgeProperties (line 375)
Edge properties must be a table.

Error in graph/set.EdgeProperties (line 330)
            G.EdgeProperties = graph.validateEdgeProperties(T);

Error in graph (line 278)
                    G.EdgeProperties = table(nonzeros(tril(A)), 'VariableNames',
                    {'Weight'});

Error in GradientMaps/approaches (line 275)
            if ~all(conncomp(graph(abs(data),'lower')) == 1)

Error in GradientMaps/fit (line 106)
            approaches(obj,kernel_data,approach_arg{:});
```


Discussion / hints:

Fix patch:
diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index 334d9ec..eb10d91 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -1,9 +1,18 @@
+v0.1.4, April 2022 -- Bug Fixes
+MATLAB:
+  - Fixed a bug that caused the detection of connected affinity matrices to error.
+
+v0.1.3, January 2022 -- Fix dependency error
+Python:
+  - Fixed an issue caused by an update to scikit-learn's kmeans function.
+
+
 Hotfix, September 2021 -- MATLAB eigenvalues
-MATLAB
+MATLAB:
 - Fixed an inaccuracy in the computation of the eigenvalues in diffusion mapping.
 
 v0.1.2, July 2022
-MATLAB
+MATLAB:
   - Added a verbose flag to GradientMaps (default value: false).
   - plot_hemispheres has several new functionalities:
       - Now returns an object rather than the graphics handles.
diff --git a/docs/pages/matlab_doc/examples/tutorial_1_gradient_creation.rst b/docs/pages/matlab_doc/examples/tutorial_1_gradient_creation.rst
index ccb69c2..f6bc5d7 100644
--- a/docs/pages/matlab_doc/examples/tutorial_1_gradient_creation.rst
+++ b/docs/pages/matlab_doc/examples/tutorial_1_gradient_creation.rst
@@ -27,7 +27,7 @@ Let's first look at the parcellation scheme we're using.
 .. code-block:: matlab    
     
     h = plot_hemispheres(labeling, {surf_lh,surf_rh});
-    colormap(h.figure,lines(401))
+    colormap(h.handles.figure,lines(401))
 
 .. image:: ./example_figs/schaefer_400.png
     :scale: 70%
diff --git a/matlab/@GradientMaps/GradientMaps.m b/matlab/@GradientMaps/GradientMaps.m
index cb90844..b302ce4 100644
--- a/matlab/@GradientMaps/GradientMaps.m
+++ b/matlab/@GradientMaps/GradientMaps.m
@@ -286,11 +286,8 @@
                 end
             end
             
-            % Check if the graph is connected. Large matrices may remain
-            % floating point asymmetric despite the above check, so only
-            % use lower.
-            if ~all(conncomp(graph(abs(data),'lower')) == 1) 
-                error('Graph is not connected.')
+            if ~graph_is_connected(data)
+                error('Affinity matrix is not a connected graph.');
             end
             
             %% Embedding.
diff --git a/matlab/analysis_code/graph_is_connected.m b/matlab/analysis_code/graph_is_connected.m
new file mode 100644
index 0000000..cc7d320
--- /dev/null
+++ b/matlab/analysis_code/graph_is_connected.m
@@ -0,0 +1,29 @@
+function is_connected = graph_is_connected(G)
+% GRAPH_IS_CONNECTED    Assesses whether a graph is fully connected.
+%
+%   is_connected = GRAPH_IS_CONNECTED(graph) tests whether the graph is
+%   fully connected. Graph must be a graph, a 2D logical matrix, or a data type that
+%   may be converted to a 2D logical matrix; is_connected is true if the graph is
+%   connected, otherwise false.
+
+if isa(G, 'graph')
+    G = adjacency(G);
+end
+
+vertex = 1;
+visited = zeros(size(G, 1), 1, 'logical');
+
+visited = depth_first_search(G, vertex, visited);
+is_connected = all(visited);
+end
+
+function visited = depth_first_search(graph, vertex, visited)
+% Conducts a depth first search on the graph.
+visited(vertex) = true;
+adjacent_vertices = find(graph(vertex, :));
+for adjacent_vertex = adjacent_vertices
+    if ~visited(adjacent_vertex)
+        visited = depth_first_search(graph, adjacent_vertex, visited);
+    end
+end
+end
\ No newline at end of file


Test patch:
diff --git a/matlab/tests/@utils_tests/utils_tests.m b/matlab/tests/@utils_tests/utils_tests.m
new file mode 100644
index 0000000..22bd244
--- /dev/null
+++ b/matlab/tests/@utils_tests/utils_tests.m
@@ -0,0 +1,34 @@
+classdef utils_tests < matlab.unittest.TestCase
+
+    methods(Test)
+        function test_graph_is_connected(testCase)
+            % Tests the graph_is_connected function against MathWorks'
+            % conncomp.
+            
+            one_disconnected = ones(5);
+            one_disconnected(:,5) = 0;
+            one_disconnected(5,:) = 0;
+
+            test_graphs = {
+                magic(5) + magic(5)', ... % connected
+                eye(5), ... % Not connected
+                one_disconnected, ...
+                graph(one_disconnected)
+            };
+
+            for G = test_graphs
+                if isa(G{1}, 'graph')
+                    mathworks_answer = all(conncomp(G{1})==1);
+                else
+                    mathworks_answer = all(conncomp(graph(G{1}))==1);
+                end
+                brainspace_answer = graph_is_connected(G{1});
+
+                testCase.assertEqual( ...
+                    mathworks_answer, ...
+                    brainspace_answer ...
+                );
+            end
+        end
+    end
+end
\ No newline at end of file