We would like to make it easier to use libflatsurf objects in sage-flatsurf. In particular, we would like to nicely expose the more complex objects such as FlowDecomposition and Deformation to sage-flatsurf.
Almost all objects in libflatsurf are attached to a FlatTriangulation. So once we have a good translation between surfaces in sage-flatsurf and FlatTriangulation, everything else should be relatively easy to convert as well.
There are some major annoyances at building such a bridge between surfaces:
libflatsurf only knows about triangulated finite translation surfaces, sage-flatsurf covers much more than that (this could eventually be fixed in libflatsurf.)
libflatsurf labels half edges, sage-flatsurf assigns arbitrary labels to the polygons underlying a surface
sage-flatsurf surfaces could be UniqueRepresentation (if we force them to be immutable) this is essentially impossible on the C++ side.
…?
To work around these problems, we are going to make links between sage-flatsurf and libflatsurf quite explicit:
If we start with a TranslationSurface TS, we can produce a (cached) FlatTriangulation FT: FT = TS._to_pyflatsurf().libflatsurf. We can get a triangulation TranslationSurface from FT but this usually won't be the surface we started with (TS probably was not triangulated and also we pick a specific labeling that might be different.) TS_ = TranslationSurface._from_pyflatsurf(FT).sage_flatsurf.
Sending Objects Across the Bridge
So both _to_pyflatsurf() and _from_pyflatsurf() create a FlatsurfBridge (is that a better name than FlatsurfConverter?) that establishes an explicit link between a libflatsurf FlatTriangulation and a sage-flatsurf TranslationSurface. The idea is then that we can send arbitrary objects across this bridge to make sense of them on the other side.
Lifetime / Caching of FlatTriangulation/TranslationSurface
A FlatsurfBridge keeps both the TranslationSurface and the FlatTriangulation alive with strong references.
A TranslationSurface's _to_pyflatsurf() is a cached method so it holds a strong reference to the FlatsurfBridge and thus to the FlatTriangulation.
The static _from_pyflatsurf() keeps its result as a strong reference in FlatTriangulation._sage_flatsurf. There is, therefore, a circular reference between FlatTriangulation and TranslationSurface. Since none of these references are stored globally, there should be no problem with this and Python's garbage collector should be able to handle this.
(Note that FlatTriangulation is mutable. We just need to be careful not to mutate it to make this work. But this is a general problem in libflatsurf where lots of places have the assumption that the underlying surface does not change.)
Lifetime / Caching of Dependent Objects
We can do essentially the same caching scheme for dependent objects, say a SaddleConnection. Calling the cached method _to_pyflatsurf() on a sage-flatsurf saddle connection produces a libflatsurf SaddleConnection. Calling _from_pyflatsurf(connection) produces a sage-flatsurf SaddleConnection and stores the result in the _sage_flatsurf attribute in the cppyy object's __dict__.
176 tries to solve this problem. Here, I tried to create an issue that captures what the problem actually is and what is our plan to solve this. (Because the discussion was a bit scattered there and I had forgotten what we had discussed to some extent.)
We would like to make it easier to use libflatsurf objects in sage-flatsurf. In particular, we would like to nicely expose the more complex objects such as FlowDecomposition and Deformation to sage-flatsurf.
Almost all objects in libflatsurf are attached to a
FlatTriangulation
. So once we have a good translation between surfaces in sage-flatsurf andFlatTriangulation
, everything else should be relatively easy to convert as well.There are some major annoyances at building such a bridge between surfaces:
To work around these problems, we are going to make links between sage-flatsurf and libflatsurf quite explicit:
If we start with a TranslationSurface
TS
, we can produce a (cached) FlatTriangulationFT
:FT = TS._to_pyflatsurf().libflatsurf
. We can get a triangulation TranslationSurface fromFT
but this usually won't be the surface we started with (TS
probably was not triangulated and also we pick a specific labeling that might be different.)TS_ = TranslationSurface._from_pyflatsurf(FT).sage_flatsurf
.Sending Objects Across the Bridge
So both
_to_pyflatsurf()
and_from_pyflatsurf()
create aFlatsurfBridge
(is that a better name thanFlatsurfConverter
?) that establishes an explicit link between a libflatsurf FlatTriangulation and a sage-flatsurf TranslationSurface. The idea is then that we can send arbitrary objects across this bridge to make sense of them on the other side.Lifetime / Caching of FlatTriangulation/TranslationSurface
A FlatsurfBridge keeps both the TranslationSurface and the FlatTriangulation alive with strong references.
A TranslationSurface's
_to_pyflatsurf()
is a cached method so it holds a strong reference to the FlatsurfBridge and thus to the FlatTriangulation.The static
_from_pyflatsurf()
keeps its result as a strong reference inFlatTriangulation._sage_flatsurf
. There is, therefore, a circular reference betweenFlatTriangulation
andTranslationSurface
. Since none of these references are stored globally, there should be no problem with this and Python's garbage collector should be able to handle this.(Note that
FlatTriangulation
is mutable. We just need to be careful not to mutate it to make this work. But this is a general problem in libflatsurf where lots of places have the assumption that the underlying surface does not change.)Lifetime / Caching of Dependent Objects
We can do essentially the same caching scheme for dependent objects, say a SaddleConnection. Calling the cached method
_to_pyflatsurf()
on a sage-flatsurf saddle connection produces a libflatsurf SaddleConnection. Calling_from_pyflatsurf(connection)
produces a sage-flatsurf SaddleConnection and stores the result in the_sage_flatsurf
attribute in the cppyy object's__dict__
.176 tries to solve this problem. Here, I tried to create an issue that captures what the problem actually is and what is our plan to solve this. (Because the discussion was a bit scattered there and I had forgotten what we had discussed to some extent.)