Skip to content

Commit

Permalink
Change USE_EXACTPREDICATES to PREDICATES (#137)
Browse files Browse the repository at this point in the history
* Change USE_EXACTPREDICATES to PREDICATES

* news

* fix

* fix
  • Loading branch information
DanielVandH authored Jul 8, 2024
1 parent c02d20c commit 1dcf3bb
Show file tree
Hide file tree
Showing 23 changed files with 96 additions and 71 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ jobs:
arch:
- x64
preferences:
- 'default'
- 'true'
- 'false'
- 'DEFAULT'
- 'EXACT'
- 'INEXACT'
steps:
- name: Checkout repository for access
uses: actions/checkout@v4
Expand All @@ -50,7 +50,7 @@ jobs:
- name: Run the tests
uses: julia-actions/julia-runtest@v1
env:
USE_EXACTPREDICATES: ${{ matrix.preferences }}
PREDICATES: ${{ matrix.preferences }}

- name: Process the coverage
if: always()
Expand Down
2 changes: 1 addition & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## v.1.1.0

- Added the option to disable ExactPredicates.jl using Preferences.jl. See [#131](https://github.com/JuliaGeometry/DelaunayTriangulation.jl/pull/131).
- Added the option to disable ExactPredicates.jl using Preferences.jl. See [#131](https://github.com/JuliaGeometry/DelaunayTriangulation.jl/pull/131) and [#137](https://github.com/JuliaGeometry/DelaunayTriangulation.jl/pull/137).
- Added `DelauanyTriangulation.validate_triangulation` for validating triangulations. See [#131](https://github.com/JuliaGeometry/DelaunayTriangulation.jl/pull/131).
- Fixed a bug with the currently unused `orient(p, q, r, s)` predicate. See [#131](https://github.com/JuliaGeometry/DelaunayTriangulation.jl/pull/131).
- Added private functions `getz`, `_getz`, `getxyz`, and `_getxyz`. See [#131](https://github.com/JuliaGeometry/DelaunayTriangulation.jl/pull/131).
Expand Down
2 changes: 1 addition & 1 deletion docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ const _PAGES = [
"Geometrical Predicates" => "manual/predicates.md",
"Triangulation Output" => "manual/triangulation_output.md",
"Voronoi Tessellation Output" => "manual/voronoi_output.md",
"Disabling Exact Predicates" => "manual/disabling_ea.md",
"Disabling Robust Predicates" => "manual/disabling_ea.md",
],
"API Reference" => [
"Section Overview" => "api/overview.md",
Expand Down
2 changes: 1 addition & 1 deletion docs/src/extended/utils.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,5 @@ DefaultAdjacentValue
GhostVertex
ε
USE_EXACTPREDICATES
PREDICATES
```
4 changes: 2 additions & 2 deletions docs/src/literate_tutorials/convex.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ tri = triangulate_convex(points, 1:7)
#-
fig, ax, sc = triplot(tri)
fig
load_preference(DelaunayTriangulation, "USE_EXACTPREDICATES", true) && @test_reference joinpath(fig_path, "convex_ex_1.png") fig #src
!USE_INEXACTPREDICATES && @test_reference joinpath(fig_path, "convex_ex_1.png") fig #src

# This `tri` is our triangulation of the convex polygon.
# The first input is the set of points, and `S` defines
Expand All @@ -45,7 +45,7 @@ tri = triangulate_convex(points, S)
#-
fig, ax, sc = triplot(tri)
fig
load_preference(DelaunayTriangulation, "USE_EXACTPREDICATES", true) && @test_reference joinpath(fig_path, "convex_ex_2.png") fig #src
!USE_INEXACTPREDICATES && @test_reference joinpath(fig_path, "convex_ex_2.png") fig #src

# Here is a comparison of the time it takes to triangulate this
# using `triangulate_convex` or `triangulate`.
Expand Down
8 changes: 4 additions & 4 deletions docs/src/literate_tutorials/point_location.jl
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ V = find_triangle(tri, q)
fig, ax, sc = triplot(tri, show_ghost_edges=true)
scatter!(ax, q)
fig
load_preference(DelaunayTriangulation, "USE_EXACTPREDICATES", true) && @test_reference joinpath(fig_path, "point_location_ex_1.png") fig #src
!USE_INEXACTPREDICATES && @test_reference joinpath(fig_path, "point_location_ex_1.png") fig #src

# ## Region with concave boundaries and holes
# Now we give an example of point location for a reason with holes. Since the
Expand All @@ -105,7 +105,7 @@ refine!(tri; max_area=0.01get_area(tri), rng);
# To demonstrate this, see the following plot:
fig, ax, sc = triplot(tri, show_ghost_edges=true)
fig
load_preference(DelaunayTriangulation, "USE_EXACTPREDICATES", true) && @test_reference joinpath(fig_path, "point_location_ex_2.png") fig #src
!USE_INEXACTPREDICATES && @test_reference joinpath(fig_path, "point_location_ex_2.png") fig #src

# The ghost edges now intersect the boundary, which doesn't make sense, and creates difficulties.
# Let us now demonstrate how the function still works here. We try finding the blue points shown below.
Expand All @@ -118,7 +118,7 @@ qs = [
fig, ax, sc = triplot(tri, show_ghost_edges=false)
scatter!(ax, qs, color=:blue, markersize=16)
fig
load_preference(DelaunayTriangulation, "USE_EXACTPREDICATES", true) && @test_reference joinpath(fig_path, "point_location_ex_3.png") fig #src
!USE_INEXACTPREDICATES && @test_reference joinpath(fig_path, "point_location_ex_3.png") fig #src

# Now let's find the triangles.
Vs = [find_triangle(tri, q; rng) for q in qs]
Expand Down Expand Up @@ -182,7 +182,7 @@ qs = [
fig, ax, sc = triplot(tri)
scatter!(ax, qs, color=:blue, markersize=16)
fig
load_preference(DelaunayTriangulation, "USE_EXACTPREDICATES", true) && @test_reference joinpath(fig_path, "point_location_ex_4.png") fig by=psnr_equality(10) #src
!USE_INEXACTPREDICATES && @test_reference joinpath(fig_path, "point_location_ex_4.png") fig by=psnr_equality(10) #src

# Here are the `find_triangle` results.
Vs = [find_triangle(tri, q; rng, concavity_protection=true) for q in qs]
Expand Down
24 changes: 12 additions & 12 deletions docs/src/manual/disabling_ea.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
CurrentModule = DelaunayTriangulation
```

# Disabling Exact Predicates
# Disabling Robust Predicates

For performance reasons, you may find it useful to want to disable exact predicates using [ExactPredicates.jl](https://github.com/lairez/ExactPredicates.jl). This can be easily done using a setup with [Preferences.jl](https://github.com/JuliaPackaging/Preferences.jl), but before you consider disabling exact predicates, there are a few things to be aware of. If you just want to disable them without reading a lot of information warning you about the consequences, please skip to the end.
For performance reasons, you may find it useful to want to disable robust predicates using [ExactPredicates.jl](https://github.com/lairez/ExactPredicates.jl). This can be easily done using a setup with [Preferences.jl](https://github.com/JuliaPackaging/Preferences.jl), but before you consider disabling robust predicates, there are a few things to be aware of. If you just want to disable them without reading a lot of information warning you about the consequences, please skip to the end. (In future versions, we may also allow use for adaptive predicates rather than exact predicates.)

## Why use exact predicates?
## Why use robust predicates?

Three great resources for understanding why we need exact predicates are
Three great resources for understanding why we need robust predicates are

1. Jonathan Shewchuk's paper on adaptive precision floating-point arithmetic [here](https://doi.org/10.1007/PL00009321).
2. Jonathan Shewchuk's lecture notes on geometric robustness [here](https://people.eecs.berkeley.edu/~jrs/meshpapers/robnotes.pdf).
Expand Down Expand Up @@ -40,7 +40,7 @@ Another issue is due to the fact that floating point arithmetic is not associati
O_{pqr} = O_{qrp} = O_{rpq},
```

but this is not true in floating point arithmetic. This causes issues with consistency - a point may be found to be both left and right of an edge depending on the order of the points given to the `orient` predicate, inevitably leading to an invalid triangulation. With the use of exact predicates, this property is guaranteed to hold, ensuring that all the predicate results are consistent with each other. This has the following consequence: **Even if you think exact predicates are not necessary for you because none of your inputs are exact (for example), you still want them to guarantee consistency with predicates regardless of the input order**.
but this is not true in floating point arithmetic. This causes issues with consistency - a point may be found to be both left and right of an edge depending on the order of the points given to the `orient` predicate, inevitably leading to an invalid triangulation. With the use of robust predicates, this property is guaranteed to hold, ensuring that all the predicate results are consistent with each other. This has the following consequence: **Even if you think robust predicates are not necessary for you because none of your inputs are exact (for example), you still want them to guarantee consistency with predicates regardless of the input order**.

## Will disabling exact predicates give me better performance?

Expand All @@ -50,28 +50,28 @@ One case where you could see improvements would be if there were many collinear

You should always benchmark your problems to see if disabling exact predicates, if you choose to do, will actually give you better performance.

## How do I disable exact predicates?
## How do I disable robust predicates?

If you still want to disable exact predicates, here is how you can do so. Before doing `using DelaunayTriangulation`, you can use
If you still want to disable robust predicates, here is how you can do so. Before doing `using DelaunayTriangulation`, you can use

```julia
using Preferences: set_preferences!
set_preferences!("DelaunayTriangulation", "USE_EXACTPREDICATES" => false)
set_preferences!("DelaunayTriangulation", "PREDICATES" => "INEXACT")
using DelaunayTriangulation # only load after setting the preference
```

The `set_preferences!` call will make a `LocalPreferences.toml` file in your directory that sets this preference. Once this file exists you are free to delete `Preferences.jl`. If you want to skip using Preferences.jl entirely, you can also just create `LocalPreferences.toml` in your working directory manually and put
(The reason we don't use a Boolean approach and do something like `"USE_EXACTPREDICATES => true"` is in case we want to add another type of method for computing predicates such as adaptive rather than exact methods.) The `set_preferences!` call will make a `LocalPreferences.toml` file in your directory that sets this preference. Once this file exists you are free to delete `Preferences.jl`. If you want to skip using Preferences.jl entirely, you can also just create `LocalPreferences.toml` in your working directory manually and put

```
[DelaunayTriangulation]
USE_EXACTPREDICATES = false
PREDICATES = "INEXACT"
```

into it. If you later want to re-enable exact predicates, either delete the file or write `USE_EXACTPREDICATES = true` instead. This setup ensures that there is no slowdown in the package from checking if ExactPredicates.jl is being used at runtime, as it is all done at compile time instead.
into it. If you later want to re-enable robust predicates, either delete the file or write `PREDICATES = "EXACT"` instead. This setup ensures that there is no slowdown in the package from checking if robust predicates are being used at runtime, as it is all done at compile time instead.

## Can I check if my computed triangulation is valid?

When you are not using exact predicates, you may want to check if your computed triangulation is actually a valid Delaunay triangulation. We provide the function `DelaunayTriangulation.validate_triangulation` for this purpose. This functionality is quite slow to use and is not currently optimised or well-documented (contributions towards addressing these issues are welcome), but it will work. One important note is that this check does actually use predicates in certain areas, so this check is still not guaranteed to be 100% accurate. Here is an example of its use.
When you are not using robust predicates, you may want to check if your computed triangulation is actually a valid Delaunay triangulation. We provide the function `DelaunayTriangulation.validate_triangulation` for this purpose. This functionality is quite slow to use and is not currently optimised or well-documented (contributions towards addressing these issues are welcome), but it will work. One important note is that this check does actually use predicates in certain areas, so this check is still not guaranteed to be 100% accurate without robust predicates. Here is an example of its use.

```julia
using DelaunayTriangulation
Expand Down
2 changes: 1 addition & 1 deletion docs/src/manual/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ This section gives a manual for certain parts of the package. Little code will b
- [Geometrical Predicates](predicates.md): How geometric predicates are defined in this package.
- [Triangulation Output](triangulation_output.md): How the output of a triangulation is defined.
- [Voronoi Tessellation Output](voronoi_output.md): How the output of a Voronoi tessellation is defined.
- [Disabling Exact Predicates](disabling_ea.md): How to disable ExactPredicates.jl.
- [Disabling Robust Predicates](disabling_ea.md): How to disable the use of robust predicates.
2 changes: 2 additions & 0 deletions docs/src/manual/predicates.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ CurrentModule = DelaunayTriangulation

This section discusses how geometrical predicates are defined in this package. The predicates in this package are primarily derived from those implemented in [ExactPredicates.jl](https://github.com/lairez/ExactPredicates.jl). The choice of exact predicates is important for the robustness of the algorithms in this package. Without using exact predicates, you may quickly find issues such as infinite loops or errors in the algorithms, as discussed for example at the start of p.3 of [these notes](https://perso.uclouvain.be/jean-francois.remacle/LMECA2170/robnotes.pdf) by Shewchuk. If you do want to disable exact predicates, see [here](disabling_ea.md).

Please also note that the use of ExactPredicates.jl for computing predicates is not part of the API - it is possible that a new system is used in the future for computing predicates. With the `PREDICATES` preference, defined [here](disabling_ea.md), we allow for this possibility; the default for this preference is also not part of the public API.

## Certificates

All predicates defined in this package return a [`Certificate`](@ref) which simply specifies the result of the predicate. This is easier than working with `Bool`s only as (1) not all predicates have only two outcomes and (2) it is easier to see the certificate than to remember exactly what outcome is represented by a `Bool`. For example:
Expand Down
10 changes: 5 additions & 5 deletions src/predicates/exactpredicates_definitions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ orient_predicate(::Any, ::Any, ::Any)

@static if USE_EXACTPREDICATES
orient_predicate(p, q, r) = orient(_getxy(p), _getxy(q), _getxy(r))
else
elseif USE_INEXACTPREDICATES
orient_predicate(p, q, r) = _orient_predicate(_getxy(p), _getxy(q), _getxy(r))
end
function _orient_predicate(p, q, r)
Expand Down Expand Up @@ -152,7 +152,7 @@ orient_predicate(::Any, ::Any, ::Any, ::Any)

@static if USE_EXACTPREDICATES
orient_predicate(p, q, r, s) = orient(_getxyz(p), _getxyz(q), _getxyz(r), _getxyz(s))
else
elseif USE_INEXACTPREDICATES
orient_predicate(p, q, r, s) = _orient_predicate(_getxyz(p), _getxyz(q), _getxyz(r), _getxyz(s))
end
function _orient_predicate(p, q, r, s)
Expand Down Expand Up @@ -190,7 +190,7 @@ incircle_predicate

@static if USE_EXACTPREDICATES
incircle_predicate(a, b, c, p) = incircle(_getxy(a), _getxy(b), _getxy(c), _getxy(p))
else
elseif USE_INEXACTPREDICATES
incircle_predicate(a, b, c, p) = _incircle_predicate(_getxy(a), _getxy(b), _getxy(c), _getxy(p))
end
function _incircle_predicate(p, q, r, a)
Expand Down Expand Up @@ -225,7 +225,7 @@ parallelorder_predicate

@static if USE_EXACTPREDICATES
parallelorder_predicate(a, b, p, q) = parallelorder(_getxy(a), _getxy(b), _getxy(p), _getxy(q))
else
elseif USE_INEXACTPREDICATES
parallelorder_predicate(a, b, p, q) = _parallelorder_predicate(_getxy(a), _getxy(b), _getxy(p), _getxy(q))
end
function _parallelorder_predicate(a, b, p, q)
Expand Down Expand Up @@ -309,7 +309,7 @@ end
ExactPredicates.Codegen.group!(qr...)
return pr[1] * qr[1] + pr[2] * qr[2]
end
else
elseif USE_INEXACTPREDICATES
function angle_is_acute(p, q, r)
px, py = _getxy(p)
qx, qy = _getxy(q)
Expand Down
39 changes: 25 additions & 14 deletions src/setup.jl
Original file line number Diff line number Diff line change
Expand Up @@ -52,33 +52,44 @@ toggle_inf_warn!() = (INF_WARN[] = !INF_WARN[])
end

@static if VERSION v"1.6"
const USE_EXACTPREDICATES = @load_preference("USE_EXACTPREDICATES", true)::Bool
const PREDICATES = @load_preference("PREDICATES", "EXACT")::String # This default is not guaranteed to be consistent between versions
else
const USE_EXACTPREDICATES = true
const PREDICATES = "EXACT"
end
@static if PREDICATES ("EXACT", "INEXACT")
throw("You have set the PREDICATES option to PREDICATES = $PREDICATES. This is not allowed, only EXACT or INEXACT are possible choices.")
end

@doc """
USE_EXACTPREDICATES
PREDICATES
Type of predicates to use. This can be either
Whether to use ExactPredicates.jl for computing predicates. By default,
this is true, but a user can change this by defining a preference with Preferences.jl, i.e.
you could do the following
- `PREDICATES = "EXACT"`: Use ExactPredicates.jl.
- `PREDICATES = "INEXACT"`: Compute the predicates numerically without any extra checks.
(In the future, this may include `"ADAPTIVE"`.) By default this is assumed to be
`"EXACT"`, but this default is not guaranteed to be the same across versions.
You can change this setting using Preferences.jl. For example, to
use inexact predicates, do
```julia-repl
julia> using Preferences: set_preferences!
julia> set_preferences!("DelaunayTriangulation", "USE_EXACTPREDICATES" => false)
julia> set_preferences!("DelaunayTriangulation", "PREDICATES" => "INEXACT")
julia> using DelaunayTriangulation # load only after setting the preference
julia> DelaunayTriangulation.USE_EXACTPREDICATES
false
julia> DelaunayTriangulation.PREDICATES
"INEXACT"
```
You have set `USE_EXACTPREDICATES = $USE_EXACTPREDICATES`.
!!! note "Precision"
Even if you have disabled ExactPredicates.jl, the predicates
are still computed in Float64 precision.
Regardless of the setting, all coordinates are computed to `Float64` before
computing any predicates.
"""
USE_EXACTPREDICATES
PREDICATES

const USE_EXACTPREDICATES = PREDICATES == "EXACT"
const USE_INEXACTPREDICATES = PREDICATES == "INEXACT"

2 changes: 1 addition & 1 deletion test/constrained_triangulation/segment_location.jl
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ end
end
end

if load_preference(DelaunayTriangulation, "USE_EXACTPREDICATES", true)
if !USE_INEXACTPREDICATES
@testset "A previously broken example" begin
for m in 1:100
a = -0.1
Expand Down
2 changes: 1 addition & 1 deletion test/data_structures/curves.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1979,7 +1979,7 @@ end
end

## Closest point
if load_preference(DelaunayTriangulation, "USE_EXACTPREDICATES", true)
if !USE_INEXACTPREDICATES
for spl in (spl, pspl)
for _ in 1:500
p = randn(2) * 30 |> Tuple
Expand Down
12 changes: 11 additions & 1 deletion test/helper_functions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2203,9 +2203,19 @@ using DelaunayTriangulation:
test_adjacent_map_matches_adjacent2vertex_map,
test_each_edge_has_two_incident_triangles,
test_triangle_orientation,
test_iterators
test_iterators,
USE_INEXACTPREDICATES,
USE_EXACTPREDICATES
using Preferences
if USE_INEXACTPREDICATES
@test load_preference(DelaunayTriangulation, "PREDICATES", "EXACT") == "INEXACT"
elseif USE_EXACTPREDICATES
@test load_preference(DelaunayTriangulation, "PREDICATES", "EXACT") == "EXACT"
end

export validate_triangulation
export USE_INEXACTPREDICATES
export USE_EXACTPREDICATES
export @_adj
export _make_graph_from_adjacency
export get_random_convex_polygon
Expand Down
2 changes: 1 addition & 1 deletion test/operations/delete_holes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ end
validate_statistics(tri)
end

if load_preference(DelaunayTriangulation, "USE_EXACTPREDICATES", true)
if !USE_INEXACTPREDICATES
@testset "Interior holes that were already triangles" begin
p1 = (0.0, 0.0)
p2 = (1.0, 0.0)
Expand Down
2 changes: 1 addition & 1 deletion test/operations/delete_point.jl
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ using StaticArrays
end
end

if load_preference(DelaunayTriangulation, "USE_EXACTPREDICATES", true)
if !USE_INEXACTPREDICATES
@testset "Lattice with a single boundary index" begin
for j in 1:20
rng1 = StableRNG(j)
Expand Down
4 changes: 2 additions & 2 deletions test/point_location/jump_and_march.jl
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ rep[3].y = mean([12.0, 6.0, 2.0, 4.0, 6.0, 10.0])
rep[3].y = mean([12.0, 6.0, 2.0, 4.0, 6.0, 10.0])
end

if load_preference(DelaunayTriangulation, "USE_EXACTPREDICATES", true)
if !USE_INEXACTPREDICATES
@testset "Test that we can find a point in every triangle" begin
for run in 1:6
for V in each_triangle(tri.triangles)
Expand Down Expand Up @@ -165,7 +165,7 @@ rep[3].y = mean([12.0, 6.0, 2.0, 4.0, 6.0, 10.0])
end
end

if !load_preference(DelaunayTriangulation, "USE_EXACTPREDICATES", true) && tri_idx 3
if !!USE_INEXACTPREDICATES && tri_idx 3
@testset "Test that we don't break for points already in the triangulation" begin
for _ in 1:6
for k in DT.each_solid_vertex(tri)
Expand Down
Loading

0 comments on commit 1dcf3bb

Please sign in to comment.