Skip to content

Commit

Permalink
nngraph: fix symmetrization
Browse files Browse the repository at this point in the history
Symmetrizing with the average was setting the distance between v1 and v2
as the average between 0 and the true distance if v1 was the k-NN of v2
but v2 was not in the k-NN of v1.

Moreover, symmetrizing before taking the mean assures that every
distance is counted twice (only some would be counted twice otherwise).
  • Loading branch information
mdeff committed Feb 16, 2019
1 parent 57ce98c commit 695272b
Show file tree
Hide file tree
Showing 5 changed files with 10 additions and 10 deletions.
4 changes: 2 additions & 2 deletions doc/tutorials/optimization.rst
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ We start with the graph TV regularization. We will use the :class:`pyunlocbox.so
>>> prob1 = pyunlocbox.solvers.solve([d, r, f], solver=solver,
... x0=x0, rtol=0, maxit=1000)
Solution found after 1000 iterations:
objective function f(sol) = 2.250584e+02
objective function f(sol) = 2.138668e+02
stopping criterion: MAXIT
>>>
>>> fig, ax = G.plot(prob1['sol'])
Expand All @@ -107,7 +107,7 @@ This figure shows the label signal recovered by graph total variation regulariza
>>> prob2 = pyunlocbox.solvers.solve([r, f], solver=solver,
... x0=x0, rtol=0, maxit=1000)
Solution found after 1000 iterations:
objective function f(sol) = 6.504290e+01
objective function f(sol) = 7.413918e+01
stopping criterion: MAXIT
>>>
>>> fig, ax = G.plot(prob2['sol'])
Expand Down
4 changes: 2 additions & 2 deletions pygsp/filters/filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ def filter(self, s, method='chebyshev', order=30):
>>> _ = G.plot(s1, ax=axes[0])
>>> _ = G.plot(s2, ax=axes[1])
>>> print('{:.5f}'.format(np.linalg.norm(s1 - s2)))
0.26808
0.28049
Perfect reconstruction with Itersine, a tight frame:
Expand Down Expand Up @@ -449,7 +449,7 @@ def estimate_frame_bounds(self, x=None):
A=1.708, B=2.359
>>> A, B = g.estimate_frame_bounds(G.e)
>>> print('A={:.3f}, B={:.3f}'.format(A, B))
A=1.723, B=2.359
A=1.712, B=2.348
The frame bounds can be seen in the plot of the filter bank as the
minimum and maximum of their squared sum (the black curve):
Expand Down
2 changes: 1 addition & 1 deletion pygsp/graphs/fourier.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def coherence(self):
>>> graph.compute_fourier_basis()
>>> minimum = 1 / np.sqrt(graph.n_vertices)
>>> print('{:.2f} in [{:.2f}, 1]'.format(graph.coherence, minimum))
0.88 in [0.12, 1]
0.93 in [0.12, 1]
>>>
>>> # Plot the most localized eigenvector.
>>> import matplotlib.pyplot as plt
Expand Down
8 changes: 4 additions & 4 deletions pygsp/graphs/nngraphs/nngraph.py
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,10 @@ def __init__(self, features, standardize=False,
start = end
W = sparse.csr_matrix((value, (row, col)), (n_vertices, n_vertices))

# Enforce symmetry. May have been broken by k-NN. Checking symmetry
# with np.abs(W - W.T).sum() is as costly as the symmetrization itself.
W = utils.symmetrize(W, method='fill')

if kernel_width is None:
kernel_width = np.mean(W.data) if W.nnz > 0 else np.nan
# Alternative: kernel_width = radius / 2 or radius / np.log(2).
Expand All @@ -350,10 +354,6 @@ def kernel(distance, width):

W.data = kernel(W.data, kernel_width)

# Enforce symmetry. May have been broken by k-NN. Checking symmetry
# with np.abs(W - W.T).sum() is as costly as the symmetrization itself.
W = utils.symmetrize(W, method='average')

# features is stored in coords, potentially standardized
self.standardize = standardize
self.metric = metric
Expand Down
2 changes: 1 addition & 1 deletion pygsp/tests/test_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ def test_modulation_gabor(self):
f2 = filters.Gabor(self._G, f)
s1 = f1.filter(self._signal)
s2 = f2.filter(self._signal)
np.testing.assert_allclose(s1, -s2, atol=1e-5)
np.testing.assert_allclose(s1, s2, atol=1e-5)

def test_halfcosine(self):
f = filters.HalfCosine(self._G, Nf=4)
Expand Down

0 comments on commit 695272b

Please sign in to comment.