# `Tempo.Network.Relation`
[🔗](https://github.com/kipcole9/tempo/blob/v0.20.0/lib/tempo/network/relation.ex#L1)

A chronological relation — a constraint between the boundaries of two
time-periods.

Relations are stored as data and translated to atomic Simple Temporal
Problem constraints (`b₁ − b₂ ≤ k`) during normalisation. The
vocabulary follows ChronoLog (Levy et al. 2020, Tables 1–5) and maps
onto Allen's interval algebra where an Allen relation exists; the
*metric* (delay) relations carry an integer offset Allen does not
express.

### Relation types

Qualitative (no parameters):

* `:contemporary` — `A` and `B` overlap (non-empty intersection).

* `:includes` / `:included_in` — `A` contains / is contained by `B`.

* `:overlaps` / `:overlapped_by` — `A` overlaps and precedes /
  follows `B` (a shared extent, neither containing the other).

* `:starts_during` / `:includes_start` — `A`'s start falls within
  `B` / `B`'s start falls within `A`.

* `:ends_during` / `:includes_end` — `A`'s end falls within `B` /
  `B`'s end falls within `A`.

* `:before` / `:after` — `A` is strictly before / after `B`
  (no overlap).

* `:immediately_precedes` / `:immediately_follows` — `end(A)=start(B)`
  and the dual.

* `:synchronous_start` / `:synchronous_end` — shared start / end
  boundary (in either direction; the loose form of Allen's
  `starts`/`started_by` and `finishes`/`finished_by`).

* `:starts` / `:started_by` — `A` and `B` share a start boundary and
  `A` ends no later than / no earlier than `B` (Allen's `starts` /
  `started_by`).

* `:finishes` / `:finished_by` — `A` and `B` share an end boundary
  and `A` starts no earlier than / no later than `B` (Allen's
  `finishes` / `finished_by`).

* `:strictly_contemporary` — `A` and `B` share a non-empty *interior*
  (overlap of positive extent, not merely a touching boundary).

* `:equals` — identical start and end.

Metric (parameterised by a duration and a comparison):

* `{:delay, edge_a, edge_b, comparison, duration}` — the `edge_a`
  boundary of `A` is `comparison` (`:exactly` | `:at_least` |
  `:at_most`) `duration` before the `edge_b` boundary of `B`, where
  each edge is `:start` or `:end`.

Boundary (a single comparison between one boundary of each period):

* `{:boundary, edge_a, comparison, edge_b}` — the `edge_a` boundary of
  `A` is `comparison` the `edge_b` boundary of `B`, where `comparison`
  is `:before` (strictly), `:at_or_before`, `:coincident` (equal),
  `:at_or_after`, or `:after` (strictly). This is the complete
  boundary-inequality lattice: any of ChronoLog's "starts/ends
  before/after/at the start/end of" synchronisms is one such relation
  (e.g. `{:boundary, :end, :at_or_before, :start}` is "ends before or
  at the start of").

# `atomic`

```elixir
@type atomic() :: {boundary(), boundary(), weight()}
```

An atomic Simple Temporal Problem constraint `from − to ≤ weight`,
one directed weighted edge `from → to` in the constraint graph.

# `boundary`

```elixir
@type boundary() :: {:start, term()} | {:end, term()} | :origin
```

A boundary variable in the constraint graph: the start or end of a
period, or the network origin `z₀`.

# `boundary_comparison`

```elixir
@type boundary_comparison() ::
  :before | :at_or_before | :coincident | :at_or_after | :after
```

A comparison between two boundaries: strictly `:before`,
`:at_or_before` (≤), `:coincident` (=), `:at_or_after` (≥), or
strictly `:after`.

# `comparison`

```elixir
@type comparison() :: :exactly | :at_least | :at_most
```

# `edge`

```elixir
@type edge() :: :start | :end
```

# `relation_type`

```elixir
@type relation_type() ::
  :contemporary
  | :includes
  | :included_in
  | :overlaps
  | :overlapped_by
  | :starts_during
  | :includes_start
  | :ends_during
  | :includes_end
  | :before
  | :after
  | :immediately_precedes
  | :immediately_follows
  | :synchronous_start
  | :synchronous_end
  | :starts
  | :started_by
  | :finishes
  | :finished_by
  | :strictly_contemporary
  | :equals
  | {:delay, edge(), edge(), comparison(), Tempo.Duration.t()}
  | {:boundary, edge(), boundary_comparison(), edge()}
```

# `t`

```elixir
@type t() :: %Tempo.Network.Relation{
  from: term(),
  metadata: map(),
  to: term(),
  type: relation_type()
}
```

# `weight`

```elixir
@type weight() ::
  integer()
  | {:duration, Tempo.Duration.t()}
  | {:neg_duration, Tempo.Duration.t()}
```

An edge weight. An integer is a fixed offset in the network's unit
(`0` for `≤`, `-1` for a strict `<`); a duration weight is resolved
to an integer by normalisation once the unit is known.

# `from_allen`

```elixir
@spec from_allen(atom()) :: relation_type()
```

The chronological relation type naming a given Allen relation.

The inverse of `to_allen/1` for the one-to-one cases. The two
direction-symmetric Allen relations (`:overlapped_by`) name the same
chronological type with the operands read in the other order.

### Examples

    iex> Tempo.Network.Relation.from_allen(:meets)
    :immediately_precedes

    iex> Tempo.Network.Relation.from_allen(:during)
    :included_in

# `new`

```elixir
@spec new(relation_type(), term(), term(), keyword()) :: t()
```

Build a relation between two periods.

### Arguments

* `type` is a relation type (see the module documentation).

* `from` and `to` are the ids of the two periods the relation
  constrains, read as "`from` *type* `to`".

### Options

* `:metadata` is an arbitrary map carried with the relation.

### Returns

* a `t:Tempo.Network.Relation.t/0`.

### Examples

    iex> relation = Tempo.Network.Relation.new(:included_in, :s1, :k1)
    iex> {relation.type, relation.from, relation.to}
    {:included_in, :s1, :k1}

# `to_allen`

```elixir
@spec to_allen(relation_type()) :: atom() | [atom()] | nil
```

The Allen interval relation(s) a chronological relation corresponds
to, for querying a solved network with `Tempo.Interval` predicates.

Returns a single Allen atom for a one-to-one correspondence, a list
when the relation is the disjunction of several Allen relations (e.g.
`:contemporary`), or `nil` for a metric relation Allen cannot express.

### Examples

    iex> Tempo.Network.Relation.to_allen(:before)
    :precedes

    iex> Tempo.Network.Relation.to_allen(:synchronous_start)
    [:starts, :started_by, :equals]

# `to_atomic`

```elixir
@spec to_atomic(t()) :: [atomic()]
```

Translate a relation into its atomic STP constraints.

Each constraint has the single shape `from − to ≤ weight` (ISO of the
paper's §3). An equality becomes two constraints; a strict `<` on the
integer time-scale becomes `≤ -1`; a metric (delay) relation carries a
duration weight that normalisation later resolves to an integer.

### Arguments

* `relation` is a `t:Tempo.Network.Relation.t/0`.

### Returns

* a list of `t:Tempo.Network.Relation.atomic/0` constraints.

### Examples

    iex> Tempo.Network.Relation.new(:before, :a, :b) |> Tempo.Network.Relation.to_atomic()
    [{{:end, :a}, {:start, :b}, -1}]

    iex> Tempo.Network.Relation.new(:immediately_precedes, :a, :b) |> Tempo.Network.Relation.to_atomic()
    [{{:end, :a}, {:start, :b}, 0}, {{:start, :b}, {:end, :a}, 0}]

    iex> Tempo.Network.Relation.new({:boundary, :end, :at_or_before, :start}, :a, :b) |> Tempo.Network.Relation.to_atomic()
    [{{:end, :a}, {:start, :b}, 0}]

---

*Consult [api-reference.md](api-reference.md) for complete listing*
