Skip to content
122 changes: 115 additions & 7 deletions GeometryOpsCore/src/types/algorithm.jl
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,80 @@ MyIndependentAlgorithm(m::Manifold; kw1 = 1, kw2 = "hello") = MyIndependentAlgor

export Algorithm, AutoAlgorithm, ManifoldIndependentAlgorithm, SingleManifoldAlgorithm, NoAlgorithm

"""
abstract type Algorithm{M <: Manifold}

The abstract supertype for all GeometryOps algorithms.
These define how to perform a particular [`Operation`](@ref).

An algorithm may be associated with one or many [`Manifold`](@ref)s.
It may either have the manifold as a field, or have it as a static parameter
(e.g. `struct GEOS <: Algorithm{Planar}`).

## Interface

All `Algorithm`s must implement the following methods:

- `rebuild(alg, manifold::Manifold)` Rebuild algorithm `alg` with a new manifold
as passed in the second argument. This may error and throw a [`WrongManifoldException`](@ref)
if the manifold is not compatible with that algorithm.
- `manifold(alg::Algorithm)` Return the manifold associated with the algorithm.
- `best_manifold(alg::Algorithm, input)`: Return the best manifold for that algorithm, in the absence of
any other context. WARNING: this may change in future and is not stable!

The actual implementation is left to the implementation of that particular [`Operation`](@ref).

## Notable subtypes

- [`AutoAlgorithm`](@ref): Tells the [`Operation`](@ref) receiving
it to automatically select the best algorithm for its input data.
- [`ManifoldIndependentAlgorithm`](@ref): An abstract supertype for an algorithm that works on any manifold.
The manifold must be stored in the algorithm for a `ManifoldIndependentAlgorithm`, and accessed via `manifold(alg)`.
- [`SingleManifoldAlgorithm`](@ref): An abstract supertype for an algorithm that only works on a
single manifold, specified in its type parameter. `SingleManifoldAlgorithm{Planar}` is a special case
that does not have to store its manifold, since that doesn't contain any information. All other
`SingleManifoldAlgorithm`s must store their manifold, since they do contain information.
- [`NoAlgorithm`](@ref): A type that indicates no algorithm is to be used, essentially the equivalent
of `nothing`.
"""
abstract type Algorithm{M <: Manifold} end

"""
manifold(alg::Algorithm)::Manifold

Return the manifold associated with the algorithm.

May be any subtype of [`Manifold`](@ref).
"""
function manifold end

# The below definition is a special case, since [`Planar`](@ref) has no contents, being a
# singleton struct.
# If that changes in the future, then this method must be deleted.
manifold(::Algorithm{<: Planar}) = Planar()

"""
best_manifold(alg::Algorithm, input)::Manifold

Return the best [`Manifold`](@ref) for the algorithm `alg` based on the given `input`.

May be any subtype of [`Manifold`](@ref).
"""
function best_manifold end

# ## Implementation of basic algorithm types

# ### `AutoAlgorithm`

"""
AutoAlgorithm{T, M <: Manifold}(manifold::M, x::T)

Indicates that the [`Operation`](@ref) should automatically select the best algorithm for
its input data, based on the passed in manifold (may be an [`AutoManifold`](@ref)) and data
`x`.

The actual implementation is left to the implementation of that particular [`Operation`](@ref).
"""
struct AutoAlgorithm{T, M <: Manifold} <: Algorithm{M}
manifold::M
x::T
Expand All @@ -52,18 +124,33 @@ end
AutoAlgorithm(m::Manifold; kwargs...) = AutoAlgorithm(m, kwargs)
AutoAlgorithm(; kwargs...) = AutoAlgorithm(AutoManifold(), kwargs)

manifold(a::AutoAlgorithm) = a.manifold
rebuild(a::AutoAlgorithm, m::Manifold) = AutoAlgorithm(m, a.x)


# ### `ManifoldIndependentAlgorithm`

"""
abstract type ManifoldIndependentAlgorithm{M <: Manifold} <: Algorithm{M}

The abstract supertype for a manifold-independent algorithm, i.e., one which may work on any manifold.

The manifold is stored in the algorithm for a `ManifoldIndependentAlgorithm`, and accessed via `manifold(alg)`.
"""
abstract type ManifoldIndependentAlgorithm{M <: Manifold} <: Algorithm{M} end

abstract type SingleManifoldAlgorithm{M <: Manifold} <: Algorithm{M} end

struct NoAlgorithm{M <: Manifold} <: Algorithm{M}
m::M
end
# ### `SingleManifoldAlgorithm`

NoAlgorithm() = NoAlgorithm(Planar()) # TODO: add a NoManifold or AutoManifold type?
# Maybe AutoManifold
# and then we have DD.format like materialization
"""
abstract type SingleManifoldAlgorithm{M <: Manifold} <: Algorithm{M}

The abstract supertype for a single-manifold algorithm, i.e., one which is known to only work
on a single manifold.

The manifold may be accessed via `manifold(alg)`.
"""
abstract type SingleManifoldAlgorithm{M <: Manifold} <: Algorithm{M} end

function (Alg::Type{<: SingleManifoldAlgorithm{M}})(m::M; kwargs...) where {M}
# successful - the algorithm is designed for this manifold
Expand All @@ -76,3 +163,24 @@ function (Alg::Type{<: SingleManifoldAlgorithm{M}})(m::Manifold; kwargs...) wher
# throw a WrongManifoldException and be done with it
throw(WrongManifoldException{typeof(m), M, Alg}())
end


# ### `NoAlgorithm`

"""
NoAlgorithm(manifold)

A type that indicates no algorithm is to be used, essentially the equivalent
of `nothing`.

Stores a manifold within itself.
"""
struct NoAlgorithm{M <: Manifold} <: Algorithm{M}
m::M
end

NoAlgorithm() = NoAlgorithm(Planar()) # TODO: add a NoManifold or AutoManifold type?

manifold(a::NoAlgorithm) = a.m
# Maybe AutoManifold
# and then we have DD.format like materialization
11 changes: 8 additions & 3 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ CoordinateTransformations = "150eb455-5306-5404-9cee-2592286d6298"
DataAPI = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a"
DelaunayTriangulation = "927a84f5-c5f4-47a5-9785-b46e178433df"
ExactPredicates = "429591f6-91af-11e9-00e2-59fbe8cec110"
Extents = "411431e0-e8b7-467b-b5e0-f676ba4f2910"
GeoFormatTypes = "68eda718-8dee-11e9-39e7-89f7f65f511f"
GeoInterface = "cf35fbd7-0cd7-5166-be24-54bfbe79505f"
GeometryBasics = "5c1252a2-5f33-56bf-86c9-59e7332b4326"
Expand All @@ -21,17 +22,20 @@ Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c"
FlexiJoins = "e37f2e79-19fa-4eb7-8510-b63b51fe0a37"
LibGEOS = "a90b1aa1-3769-5649-ba7e-abc5a9d163eb"
Proj = "c94c279d-25a6-4763-9509-64d165bea63e"
TGGeometry = "d7e755d2-3c95-4bcf-9b3c-79ab1a78647b"

[extensions]
GeometryOpsFlexiJoinsExt = "FlexiJoins"
GeometryOpsLibGEOSExt = "LibGEOS"
GeometryOpsProjExt = "Proj"
GeometryOpsTGGeometryExt = "TGGeometry"

[compat]
CoordinateTransformations = "0.5, 0.6"
DataAPI = "1"
DelaunayTriangulation = "1.0.4"
ExactPredicates = "2.2.8"
Extents = "0.1.5"
FlexiJoins = "0.1.30"
GeoFormatTypes = "0.4"
GeoInterface = "1.2"
Expand All @@ -42,8 +46,9 @@ LinearAlgebra = "1"
Proj = "1"
SortTileRecursiveTree = "0.1"
Statistics = "1"
TGGeometry = "0.1"
Tables = "1"
julia = "1.9"
julia = "1.10"

[extras]
ArchGDAL = "c9ce4bd3-c3d5-55b8-8973-c0e20141b8c3"
Expand All @@ -53,7 +58,6 @@ DimensionalData = "0703355e-b756-11e9-17c0-8b28908087d0"
Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f"
Downloads = "f43a241f-c20a-4ad4-852c-f6b1247861c6"
FlexiJoins = "e37f2e79-19fa-4eb7-8510-b63b51fe0a37"
GeoFormatTypes = "68eda718-8dee-11e9-39e7-89f7f65f511f"
GeoJSON = "61d90e0f-e114-555e-ac52-39dfb47a3ef9"
JLD2 = "033835bb-8acc-5ee8-8aae-3f567f8a3819"
LibGEOS = "a90b1aa1-3769-5649-ba7e-abc5a9d163eb"
Expand All @@ -64,7 +68,8 @@ Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
Rasters = "a3a2b9e3-a471-40c9-b274-f788e487c689"
SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f"
Shapefile = "8e980c4a-a4fe-5da2-b3a7-4b4b0353a2f4"
TGGeometry = "d7e755d2-3c95-4bcf-9b3c-79ab1a78647b"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["ArchGDAL", "CoordinateTransformations", "DataFrames", "Distributions", "DimensionalData", "Downloads", "FlexiJoins", "GeoFormatTypes", "GeoJSON", "Proj", "JLD2", "LibGEOS", "Random", "Rasters", "NaturalEarth", "OffsetArrays", "SafeTestsets", "Shapefile", "Test"]
test = ["ArchGDAL", "CoordinateTransformations", "DataFrames", "Distributions", "DimensionalData", "Downloads", "FlexiJoins", "GeoJSON", "Proj", "JLD2", "LibGEOS", "Random", "Rasters", "NaturalEarth", "OffsetArrays", "SafeTestsets", "Shapefile", "TGGeometry", "Test"]
4 changes: 3 additions & 1 deletion benchmarks/benchmark_plots.jl
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ function plot_trials(
legend_orientation = :horizontal,
legend_halign = 1.0,
legend_valign = -0.25,
legend_kws = (;),
)

xs, ys, labels = [], [], []
Expand Down Expand Up @@ -118,7 +119,8 @@ function plot_trials(
tellheight = legend_position isa Union{Tuple, Makie.Automatic} && (legend_position isa Makie.Automatic || length(legend_position) != 3) && legend_orientation == :horizontal,
halign = legend_halign,
valign = legend_valign,
orientation = legend_orientation
orientation = legend_orientation,
legend_kws...,
)
ax.xtickcolor[] = ax.xgridcolor[]
ax
Expand Down
Loading
Loading