Skip to content

Commit

Permalink
Updating documentation, make readme also form part of the hexdocs pag…
Browse files Browse the repository at this point in the history
…es. Fixing 'leafs' spelling. Simplify the @moduledoc content since it just reiterates the README which is now included in the hexdocs and is also the default front page
  • Loading branch information
Alexander Pilafian authored and Alexander Pilafian committed Oct 22, 2019
1 parent 2822bcd commit 75456c9
Show file tree
Hide file tree
Showing 8 changed files with 62 additions and 202 deletions.
17 changes: 9 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
[![LICENSE](https://img.shields.io/hexpm/l/dynamic_rtree)](https://rawcdn.githack.com/windfish-studio/rtree/1479e8660336fb0a63fc6a39185c10e1ab940d7b/LICENSE)
[![VERSION](https://img.shields.io/hexpm/v/dynamic_rtree)](https://hexdocs.pm/dynamic_rtree/api-reference.html)

# :ddrt
# :ddrt (README)

A __D__ynamic, __D__istributed [__R__-__T__ree](https://en.wikipedia.org/wiki/R-tree) (DDRT) library written in Elixir. The 'dynamic' part of the title refers to the fact that this implementation is optimized for a high volume of update operations. Put another way, this is an R-tree best suited for use with spatial data _in constant movement_. The 'distributed' part refers to the fact that this library is designed to maintain a spatial index (rtree) across a cluster of distributed elixir nodes.

The library uses [@derekkraan](https://github.com/derekkraan)'s [MerkleMap](https://github.com/derekkraan/merkle_map) and [CRDT](https://github.com/derekkraan/delta_crdt_ex) implementations to ensure reliable, "eventually consistent" distributed behavior.
Expand Down Expand Up @@ -231,10 +232,10 @@ warmup: 2 s
time: 5 s
memory time: 0 ns
parallel: 1
inputs: delete all leafs of tree [1000]
inputs: delete all leaves of tree [1000]
Estimated total run time: 28 s

##### With input delete all leafs of tree [1000] #####
##### With input delete all leaves of tree [1000] #####
Name ips average deviation median 99th %
map bulk 175.20 5.71 ms ±9.18% 5.60 ms 9.47 ms
merklemap bulk 80.27 12.46 ms ±21.27% 11.74 ms 25.37 ms
Expand All @@ -255,10 +256,10 @@ warmup: 2 s
time: 10 s
memory time: 0 ns
parallel: 1
inputs: all leafs of tree [1000], all leafs of tree [100000]
inputs: all leaves of tree [1000], all leaves of tree [100000]
Estimated total run time: 48 s

##### With input all leafs of tree [1000] #####
##### With input all leaves of tree [1000] #####
Name ips average deviation median 99th %
map 133.88 7.47 ms ±22.82% 6.92 ms 14.83 ms
merklemap 65.74 15.21 ms ±21.93% 14.18 ms 26.42 ms
Expand All @@ -267,7 +268,7 @@ Comparison:
map 133.88
merklemap 65.74 - 2.04x slower +7.74 ms

##### With input all leafs of tree [100000] #####
##### With input all leaves of tree [100000] #####
Name ips average deviation median 99th %
map 0.68 1.46 s ±15.84% 1.47 s 1.82 s
merklemap 0.33 3.01 s ±8.23% 3.09 s 3.21 s
Expand Down Expand Up @@ -332,10 +333,10 @@ warmup: 2 s
time: 5 s
memory time: 0 ns
parallel: 1
inputs: 1000 leafs
inputs: 1000 leaves
Estimated total run time: 28 s

##### With input 1000 leafs #####
##### With input 1000 leaves #####
Name ips average deviation median 99th %
map bulk 305.53 3.27 ms ±39.39% 2.79 ms 7.96 ms
merklemap bulk 190.61 5.25 ms ±62.06% 4.37 ms 17.65 ms
Expand Down
4 changes: 2 additions & 2 deletions bench/delete_benchmark.ex
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ Benchee.run(%{
end},

}, inputs: %{
cyan() <>"delete all leafs of tree ["<> color(195) <>"1000" <> cyan() <> "]" <> reset() => generate.(1000,1),
#cyan() <>"all leafs of tree ["<> color(195) <>"100000" <> cyan() <> "]" <> reset() => generate.(100000,1)
cyan() <>"delete all leaves of tree ["<> color(195) <>"1000" <> cyan() <> "]" <> reset() => generate.(1000,1),
#cyan() <>"all leaves of tree ["<> color(195) <>"100000" <> cyan() <> "]" <> reset() => generate.(100000,1)
}
)
8 changes: 4 additions & 4 deletions bench/insert_benchmark.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ generate = fn n,s ->
BoundingBoxGenerator.generate(n,s,[]) |> Enum.map(fn x -> {:os.system_time(:nanosecond),x} end)
end

new_tree = fn leafs,typ ->
new_tree = fn leaves,typ ->
DynamicRtree.new(%{type: typ})
generate.(leafs,1)
generate.(leaves,1)
|> Enum.each(fn l ->
DynamicRtree.insert(l)
end)
Expand Down Expand Up @@ -64,8 +64,8 @@ Benchee.run(%{
end}

}, inputs: %{
yellow() <> "1000 " <> green() <>"leafs" <> reset() => generate.(1000,1),
#yellow() <> "1000000 " <> green() <>"leafs" <> reset() => generate.(1000000,1)
yellow() <> "1000 " <> green() <>"leaves" <> reset() => generate.(1000,1),
#yellow() <> "1000000 " <> green() <>"leaves" <> reset() => generate.(1000000,1)
}, time: 5)


8 changes: 4 additions & 4 deletions bench/update_benchmark.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ generate = fn n,s ->
BoundingBoxGenerator.generate(n,s,[]) |> Enum.map(fn x -> {UUID.uuid1(),x} end)
end

new_tree = fn leafs,typ ->
new_tree = fn leaves,typ ->
DynamicRtree.new(%{type: typ})
DynamicRtree.insert(leafs)
DynamicRtree.insert(leaves)
end

unit_move = fn ->
Expand Down Expand Up @@ -58,6 +58,6 @@ Benchee.run(%{
end},

}, inputs: %{
cyan() <>"all leafs of tree ["<> color(195) <>"1000" <> cyan() <> "]" <> reset() => generate.(1000,1),
cyan() <>"all leafs of tree ["<> color(195) <>"100000" <> cyan() <> "]" <> reset() => generate.(100000,1)
cyan() <>"all leaves of tree ["<> color(195) <>"1000" <> cyan() <> "]" <> reset() => generate.(1000,1),
cyan() <>"all leaves of tree ["<> color(195) <>"100000" <> cyan() <> "]" <> reset() => generate.(100000,1)
}, time: 10)
95 changes: 12 additions & 83 deletions lib/ddrt.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,93 +3,22 @@ defmodule DDRT do
alias DDRT.DynamicRtree

@moduledoc """
This is the top level module, which one you should include at your application supervision tree.
If you want the distributed dynamic r-tree, start this process is a MUST.
DDRT.start_link(%{})
or
children = [
...
{DDRT, %{}}
]
Else, if you want just the dynamic r-tree this module is not a MUST, but you can use it anyways.
## Configuration
Let's talk about which parameters you can pass to init the DDRT.
`name`: the name of the r-tree.
`mode`: the mode of the r-tree. There are two:
- `:dynamic`: all the r-trees with same name in different nodes will be sync.
- `:standalone`: a dynamic r-tree that just will be at your node.
`width`: the max width (the number of childs) than can handle every node.
`type`: the type of data structure that maintains the r-tree. There are two:
- `Map`: faster way. Recommended if you don't need sync.
- `MerkleMap`: a bit slower, but perfect to get minimum r-tree modifications.
`verbose`: allows `Logger` to report console logs. (Decrease performance)
`seed`: the start seed for the middle nodes uniq ID of the r-tree. Same seed will always reach same sequence of uniq ID's.
## Distributed part
You have to config the Erlang node interconnection with `libcluster`.
The easy way is that:
- At `config.exs` define the nodes you want to connect:
use Mix.Config
config :libcluster,
topologies: [
example: [
# The selected clustering strategy. Required.
strategy: Cluster.Strategy.Epmd,
# Configuration for the provided strategy. Optional.
config: [hosts: [:"a@localhost", :"b@localhost"]],
]
]
- Then you should start you application for example like that:
eduardo@elixir_rtree $ iex --name a@localhost -S mix
iex(a@localhost)1>
eduardo@elixir_rtree $ iex --name b@localhost -S mix
iex(b@localhost)1>
Finally, if you started in both nodes a `DDRT` with the same name you can simply use the `DynamicRtree` API module and you will have the r-tree sync between nodes.
`Note`: is important that you have the same configuration for the DDRT at the different nodes.
This is the top-level `DDRT` module. Use this to create a distributed r-tree. If you're only interested in using this package for the r-tree implementation, you should instead use `DDRT.DynamicRtree`
"""


#DDRT party begins.
@doc """
DDRT party begins.
## Examples
`Note`: if you select mode distributed I'll force you anyways to use MerkleMap.
iex> DDRT.start_link(%{mode: :distributed, name: Rupert, type: MerkleMap, seed: 5})
{:ok, #PID<0.224.0>}
These are all of the possible DDRT configuration parameters and their default values.
## Default values
[
name: DynamicRtree
width: 6,
type: Map,
verbose: false,
seed: 0
]
```
[
name: DDRT,
width: 6,
verbose: false,
seed: 0
]
```
"""
@spec start_link(DynamicRtree.tree_config()) :: {:ok, pid}
def start_link(opts) do
Expand Down
92 changes: 9 additions & 83 deletions lib/ddrt/dynamic_rtree.ex
Original file line number Diff line number Diff line change
Expand Up @@ -64,81 +64,7 @@ defmodule DDRT.DynamicRtree do
name: nil

@moduledoc """
This is the API module of the elixir r-tree implementation where you can do the basic actions.
## Easy to use:
Starts a local r-tree named as Peter
iex> DDRT.start_link(%{name: Peter})
{:ok, #PID<0.214.0>}
Insert "Griffin" on r-tree named as Peter
iex> DynamicRtree.insert({"Griffin",[{4,5},{6,7}]},Peter)
{:ok,
%{
43143342109176739 => {["Griffin"], nil, [{4, 5}, {6, 7}]},
:root => 43143342109176739,
:ticket => [19125803434255161 | 82545666616502197],
"Griffin" => {:leaf, 43143342109176739, [{4, 5}, {6, 7}]}
}}
Insert "Parker" on r-tree named as Peter
iex> DynamicRtree.insert({"Parker",[{10,11},{16,17}]},Peter)
{:ok,
%{
43143342109176739 => {["Parker", "Griffin"], nil, [{4, 11}, {6, 17}]},
:root => 43143342109176739,
:ticket => [19125803434255161 | 82545666616502197],
"Griffin" => {:leaf, 43143342109176739, [{4, 5}, {6, 7}]},
"Parker" => {:leaf, 43143342109176739, [{10, 11}, {16, 17}]}
}}
Query which leafs at Peter r-tree overlap with box `[{0,7},{4,8}]`
iex> DynamicRtree.query([{0,7},{4,8}],Peter)
{:ok, ["Griffin"]}
Updates "Griffin" bounding box
iex> DynamicRtree.update("Griffin",[{-6,-5},{11,12}],Peter)
{:ok,
%{
43143342109176739 => {["Parker", "Griffin"], nil, [{-6, 11}, {6, 17}]},
:root => 43143342109176739,
:ticket => [19125803434255161 | 82545666616502197],
"Griffin" => {:leaf, 43143342109176739, [{-6, -5}, {11, 12}]},
"Parker" => {:leaf, 43143342109176739, [{10, 11}, {16, 17}]}
}}
Repeat again the last query
iex> DynamicRtree.query([{0,7},{4,8}],Peter)
{:ok, []} # Peter "Griffin" left the query bounding box
Let's punish them
iex> DynamicRtree.delete(["Griffin","Parker"],Peter)
{:ok,
%{
43143342109176739 => {[], nil, [{0, 0}, {0, 0}]},
:root => 43143342109176739,
:ticket => [19125803434255161 | 82545666616502197]
}}
## Easy concepts:
Bounding box format.
`[{x_min,x_max},{y_min,y_max}]`
Example: & & & & & y_max & & & & &
A unit at pos x: 10, y: -12 , & &
with x_size: 1 and y_size: 2 & &
would be represented with & pos &
the following bounding box x_min (x,y) x_max
[{9.5,10.5},{-13,-11}] & &
& &
& &
& & & & & y_min & & & & &
Use this module if you're interested in creating an R-Tree optimized to run on a single machine. If you'd instead like to run a distributed R-Tree on a cluster of Elixir nodes, use the `DDRT` module.
"""

def start_link(opts) do
Expand Down Expand Up @@ -195,13 +121,13 @@ defmodule DDRT.DynamicRtree do
def insert(_a, name \\ DDRT)

@doc """
Insert `leafs` at the r-tree named as `name`
Insert `leaves` at the r-tree named as `name`
Returns `{:ok,map()}`
## Parameters
- `leafs`: the data to insert.
- `leaves`: the data to insert.
- `name`: the r-tree name where you wanna insert.
## Examples
Expand Down Expand Up @@ -234,8 +160,8 @@ defmodule DDRT.DynamicRtree do
"""

def insert(leafs, name) when is_list(leafs) do
GenServer.call(name, {:bulk_insert, leafs}, :infinity)
def insert(leaves, name) when is_list(leaves) do
GenServer.call(name, {:bulk_insert, leaves}, :infinity)
end

def insert(leaf, name) do
Expand Down Expand Up @@ -274,7 +200,7 @@ defmodule DDRT.DynamicRtree do
def delete(_a, name \\ DDRT)

@doc """
Delete the leafs with the given `ids`.
Delete the leaves with the given `ids`.
Returns `{:ok,map()}`
Expand All @@ -301,7 +227,7 @@ defmodule DDRT.DynamicRtree do
end

@doc """
Update a bunch of r-tree leafs to the new bounding boxes defined.
Update a bunch of r-tree leaves to the new bounding boxes defined.
Returns `{:ok,map()}`
Expand Down Expand Up @@ -523,7 +449,7 @@ defmodule DDRT.DynamicRtree do
end

@impl true
def handle_call({:bulk_insert, leafs}, _from, state) do
def handle_call({:bulk_insert, leaves}, _from, state) do
r =
{_atom, t} =
case state.tree do
Expand All @@ -532,7 +458,7 @@ defmodule DDRT.DynamicRtree do

_ ->
final_rbundle =
leafs
leaves
|> Enum.reduce(get_rbundle(state), fn l, acc ->
%{acc | tree: acc |> tree_insert(l)}
end)
Expand Down
Loading

0 comments on commit 75456c9

Please sign in to comment.