# `Tempo.Network.Qualitative`
[🔗](https://github.com/elixir-tempo/tempo_network/blob/v1.0.0/lib/tempo/network/qualitative.ex#L1)

The bridge between Tempo's two constraint networks.

`Tempo.Network` reasons **metrically**: dated periods, bounds like
"at least twenty years after", solved completely and in polynomial time
by shortest paths. Its relation vocabulary is convex, so it cannot
represent "before **or** after".

`Tempo.Interval.RelationNetwork` reasons **qualitatively**: relation
sets over undated periods, propagated by Allen's path consistency. It
represents disjunction, and is correspondingly incomplete.

Neither subsumes the other, and each can tighten the other:

* **Metric → qualitative.** A solved metric network already excludes
  relations its bounds make impossible. `from_network/1` reads those
  out as relation sets, giving the qualitative network a much tighter
  starting point than "all thirteen".

* **Qualitative → metric.** A relation set that has narrowed to a
  single relation is a convex constraint the metric network accepts.
  `apply_to_network/2` feeds those back, where they may tighten dates
  that the metric constraints alone left loose.

`refine/1` runs the round trip.

## Example

Two periods whose order the dates leave open, plus the qualitative fact
that they cannot overlap:

    iex> alias Tempo.Network
    iex> alias Tempo.Network.Qualitative
    iex> network =
    ...>   Network.new()
    ...>   |> Network.add_period("dig", from: {:not_before, ~o"2020"}, to: {:not_after, ~o"2030"})
    ...>   |> Network.add_period("survey", from: {:not_before, ~o"2020"}, to: {:not_after, ~o"2030"})
    iex> qualitative = Qualitative.from_network(network)
    iex> is_struct(qualitative, Tempo.Interval.RelationNetwork)
    true

# `apply_to_network`

```elixir
@spec apply_to_network(Tempo.Interval.RelationNetwork.t(), Tempo.Network.t()) ::
  Tempo.Network.t()
```

Add every determined qualitative relation to a metric network.

A relation set narrowed to exactly one relation is knowledge the metric
network can use; a set with two or more still-possible relations is a
disjunction it cannot represent, and is skipped.

### Arguments

* `qualitative` is a `t:Tempo.Interval.RelationNetwork.t/0`.

* `network` is the `t:Tempo.Network.t/0` to add constraints to.

### Returns

* The updated `t:Tempo.Network.t/0`.

### Examples

    iex> network =
    ...>   Tempo.Network.new()
    ...>   |> Tempo.Network.add_period(:a, from: ~o"2000", to: ~o"2005")
    ...>   |> Tempo.Network.add_period(:b, from: ~o"2010", to: ~o"2015")
    ...>   |> Tempo.Network.add_period(:c, [])
    ...>   |> Tempo.Network.add_relation(:before, :b, :c)
    iex> {:ok, propagated} =
    ...>   network
    ...>   |> Tempo.Network.Qualitative.from_network()
    ...>   |> Tempo.Interval.RelationNetwork.propagate()
    iex> refined = Tempo.Network.Qualitative.apply_to_network(propagated, network)
    iex> Tempo.Network.Solver.relation(refined, :a, :c)
    :precedes

# `from_network`

```elixir
@spec from_network(Tempo.Network.t()) ::
  Tempo.Interval.RelationNetwork.t() | {:error, :inconsistent | Exception.t()}
```

Read a metric network out as a qualitative one.

Every pair of periods is seeded with the relations the solved metric
network still permits, so propagation starts from what the dates
already prove rather than from the full thirteen.

### Arguments

* `network` is a `t:Tempo.Network.t/0`.

### Returns

* A `t:Tempo.Interval.RelationNetwork.t/0` over the same period ids.

* `{:error, :inconsistent}` when the metric network has no solution, so
  there are no relations to read.

* `{:error, reason}` for an error the network's builders recorded.

### Examples

    iex> network =
    ...>   Tempo.Network.new()
    ...>   |> Tempo.Network.add_period(:a, from: ~o"2000", to: ~o"2005")
    ...>   |> Tempo.Network.add_period(:b, from: ~o"2010", to: ~o"2015")
    ...>   |> Tempo.Network.add_period(:c, [])
    ...>   |> Tempo.Network.add_relation(:before, :b, :c)
    iex> qualitative = Tempo.Network.Qualitative.from_network(network)
    iex> Tempo.Interval.RelationNetwork.between(qualitative, :a, :b)
    [:precedes]

# `refine`

```elixir
@spec refine(Tempo.Network.t()) ::
  {:ok, Tempo.Network.t()}
  | {:error, :inconsistent | {:inconsistent, {term(), term()}} | Exception.t()}
```

Run the round trip: read the metric network qualitatively, propagate,
and feed back what propagation determined.

This is where the two formalisms pay for each other. The metric bounds
prune relations Allen's algebra alone would leave open; propagation
then determines relations the bounds alone did not, and those become
metric constraints that can tighten dates.

### Arguments

* `network` is a `t:Tempo.Network.t/0`.

### Returns

* `{:ok, network}` — the metric network with every qualitatively
  determined relation added.

* `{:error, :inconsistent}` when the metric network has no solution.

* `{:error, {:inconsistent, pair}}` when qualitative propagation finds
  a contradiction the metric solver did not.

* `{:error, reason}` for an error the network's builders recorded.

### Examples

    iex> network =
    ...>   Tempo.Network.new()
    ...>   |> Tempo.Network.add_period(:a, from: ~o"2000", to: ~o"2005")
    ...>   |> Tempo.Network.add_period(:b, from: ~o"2010", to: ~o"2015")
    ...>   |> Tempo.Network.add_period(:c, [])
    ...>   |> Tempo.Network.add_relation(:before, :b, :c)
    iex> {:ok, refined} = Tempo.Network.Qualitative.refine(network)
    iex> Tempo.Network.Solver.relation(refined, :a, :c)
    :precedes

---

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