Skip to content

Zigzag persistence part2 - #953

Open
hschreiber wants to merge 37 commits into
GUDHI:masterfrom
hschreiber:oscillating_rips
Open

Zigzag persistence part2#953
hschreiber wants to merge 37 commits into
GUDHI:masterfrom
hschreiber:oscillating_rips

Conversation

@hschreiber

Copy link
Copy Markdown
Collaborator

Follow up of PR #917 with the addition of the oscillating Rips iterator.

The PR is still a draft as unit tests are still missing (and the doc main page could be better). I want to ask a few things @ClementMaria when he comes back before doing them.
As this depends of #917 which itself depends on #669 there is still enough to do before then...

@hschreiber
hschreiber marked this pull request as ready for review January 14, 2025 17:39
@hschreiber

Copy link
Copy Markdown
Collaborator Author

I still have to re-read trough the documentation to be sure that nothing was forgotten with the last changes, but otherwise the module should be ready to be reviewed. The unit tests were finally added and I completed the introduction of the oscillating rips filtrations.

@mglisse mglisse left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some early comments, or reminders to discuss some things, but I haven't really read the PR yet.

</tr>
</table>

### Oscillating Rips Filtrations

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want this directly on the main page, or in the Rips page (which already has sparse Rips for instance)?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a strong opinion about it. But if not every variant is displayed on the main page, we should perhaps add a mention to them in the module description of the main page. Something like:

The (Vietoris-)Rips complex is a filtered simplicial complex where every tuple of points appears with its diameter as filtration value. Alternatively, it is defined by the cliques of a graph, usually representing a metric.
This complex can be built from a point cloud and a distance function, or from a distance matrix. 

Implemented variants:
- Vietoris-Rips complex
- Sparse Rips complex
- Oscillating Rips filtration

They kind of go under otherwise...

Comment thread src/Zigzag_persistence/doc/Intro_zigzag_persistence.h Outdated
* If \f$ P \f$ is the set of points generating the rips filtration, \f$ P_i \f$ corresponds to the subset containing
* the \f$ i \f$ first points. So, at each forward inclusion, one vertex is added.
*
* The superscript of \f$ \mathcal{R} \f$ corresponds to the filtration values which will be associated to the cycles.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe say that later? First we describe the zigzag. And once that's done, we can say that we (arbitrarily?) use this as "filtration value" for the output barcode.
I assume you chose epsilon because it is monotonous, unlike the max edge length parameter, while still being homogeneous to a distance, unlike the index?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was not the one who chose the epsilon values as filtration values, but yes, I think the reason was going in this direction. And it does make sense, so I didn't contested it.

I presented it like that, because I didn't wanted to confuse the reader with theoretical details, but describing directly how it was actually implemented in Gudhi (while still making clear how the filtration looks like). In particular, because there exists other implemented variants which don't chose the exact same parametrization (for example, Dionysus, even though I can't find its documentation anymore...).

But I could invert the positions of this paragraph with the one below and be more explicit about the fact that the choice of filtration is not part of the definition, just our choice.

Comment thread src/Zigzag_persistence/doc/Intro_zigzag_persistence.h Outdated
* information about the filtration "on the fly" to avoid loading the whole filtration at once. Information about the
* current barcode can be retrieved between any steps via callback methods.
*
* \subsection zigzagrips Oscillating Rips

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want it on this page, which is more about persistence, while it is more about the geometry of building a zigzag filtration?
I am asking because Rips_complex and Persistent_cohomology are in 2 clearly separated pages.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question. I always saw it as an extension of the zigzag module, so I found it natural to put it there. But when you think about it without this point of view, the placement is a bit weird indeed. We could put it together with the Rips complex like you suggested above.

But then, should I also move the actual code ? For now the import of the ranges is via #include <gudhi/Zigzag_persistence/oscillating_rips_simplex_ranges.h> and only the helper method is imported directly with #include <gudhi/Oscillating_rips_persistence.h>.

Comment thread src/Zigzag_persistence/doc/Intro_zigzag_persistence.h Outdated
* Both multipliers have to be specified by the user.
*
* The construction is based on two types of classes:
* - @ref Oscillating_rips_edge_iterator_range and @ref Oscillating_rips_edge_vector_range_constructor computes the

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not shorten Oscillating_rips_edge_iterator_range to Oscillating_rips_edge_range? IIUC there is a range, begin on it returns Oscillating_rips_edge_iterator, an iterator is the thing that iterates on the elements of a range, everything works?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was just to contrast it with the vector version, i.e., to highlight that iterator_range $\neq$ vector_range. Same idea goes for your question below "Isn't range_ redundant in the name?".
But I am not super happy about my choice of names. So, any suggestion is welcome. It would just be nice if the difference between the two classes remain clear from the name. If possible.

* use @ref Identity_edge_modifier. Default value: @ref Identity_edge_modifier.
*/
template <typename Filtration_value, class EdgeModifier = Identity_edge_modifier>
class Oscillating_rips_edge_vector_range_constructor

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't range_ redundant in the name?

Comment on lines +79 to +80
* - @ref Oscillating_rips_edge_iterator_range and @ref Oscillating_rips_edge_vector_range_constructor computes the
* range of inserted and removed vertices and edges in the filtration based on the elements descipted above.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it explained somewhere that one stores the whole filtration in a vector while the other computes the edges lazily as you iterate on it?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is explained in the description of the classes themselves. But I am okay with repeating it here.

hschreiber and others added 5 commits February 13, 2025 13:45
Co-authored-by: Marc Glisse <marc.glisse@inria.fr>
Co-authored-by: Marc Glisse <marc.glisse@inria.fr>
Co-authored-by: Marc Glisse <marc.glisse@inria.fr>

@mglisse mglisse left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Random comments. Some may be irrelevant because I haven't read the full PR yet, but since I am going to disappear for a couple days again, I might as well post them.

Comment thread src/Zigzag_persistence/concept/DistanceFunction.h Outdated
#ifndef CONCEPT_ZZ_POINT_RANGE_H_
#define CONCEPT_ZZ_POINT_RANGE_H_

/** @file PointRange.h

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you remind me what this is for? I think doxygen knows the name of the file it is processing, and the user shouldn't care about the name of the file where the concept is documented.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It helps Doxygen to index the files better and avoids a few @ref problems. It is one of those Doxygen properties, where you don't really need it most of the time, but at some seemly random point, it will refuse referencing a method because it was missing.
It also adds a description in the file list on the website.

Comment thread src/Zigzag_persistence/concept/EdgeModifier.h Outdated
* @param f Value to modify.
* @return The modified value of @p f.
*/
static Filtration_value apply_modifier(Filtration_value f);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

static means we won't be able to use an EdgeModifier that stores data (say PowerEdgeModifier that has a field storing the desired exponent at runtime). If we don't expect to use anything other than identity or sqrt, I guess that's ok.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True. But the way it is implemented for now, the user never passes an instance of Edge_modifier to a method, only the type, from which we call Edge_modifier::apply_inverse_modifier etc. We would need to change that.
If it becomes useful, we can still add it then, I think. With an optional argument for example which won't break retro-compatibility.

Comment thread src/Zigzag_persistence/concept/StableFilteredComplex.h

private:
const std::vector<Filtration_value>* epsilonValues_; /**< Epsilon values. */
const std::vector<std::vector<std::pair<int, Filtration_value> > >* distanceMatrix_; /**< Distance matrix. */

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC this is a lower triangular matrix without diagonal (m[i][j] only valid if j<i), maybe add that as a comment?

If we wanted to, we could make it a flat vector, element (i,j) would have index i*(i-1)/2+j or a similar formula, but it may not be interesting, and it is private so can be changed later anyway.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't wanted to modify too much the initial implementation, but making the matrix flat could be a good idea. But as you say, it is private anyway as Oscillating_rips_edge_iterator_base is not really supposed to be used externally, so we can look at it later. I kept the documentation of this class public mainly to keep references to it possible, but perhaps I should just remove them?

Comment on lines +41 to +43
VECTOR, /**< The edges are computed all at once and stored in a vector. */
BOOST_RANGE /**< The edges are computed one by one at each increment of the
range iterator and therefore not stored. */

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't feel that BOOST_RANGE really conveys what this option does (I could easily wrap a vector in a boost range).
eager vs lazy?
precomputed/stored vs generated?
I don't have a very good suggestion...

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed it to ALL_AT_ONCE and ONE_BY_ONE. A bit ugly, but better than before, I guess? What do you think?

Comment thread src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h Outdated
Comment thread src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants