49 lines
1.2 KiB
Go
49 lines
1.2 KiB
Go
package dfa
|
||
|
||
import (
|
||
"fmt"
|
||
"strings"
|
||
)
|
||
|
||
// Dot returns a directed graph in [Graphviz] format that represents the finite
|
||
// join-semilattice ⟨S, ≤⟩. Vertices represent elements in S and edges
|
||
// represent the ≤ relation between elements. We map from ⟨S, ∨⟩ to ⟨S, ≤⟩ by
|
||
// computing x ∨ y for all elements in [S]², where x ≤ y iff x ∨ y == y.
|
||
//
|
||
// The resulting graph can be filtered through [tred] to compute the transitive
|
||
// reduction of the graph, the visualisation of which corresponds to the Hasse
|
||
// diagram of the semilattice.
|
||
//
|
||
// [Graphviz]: https://graphviz.org/
|
||
// [tred]: https://graphviz.org/docs/cli/tred/
|
||
func Dot[L Semilattice[Elem], Elem any](states []Elem) string {
|
||
var sb strings.Builder
|
||
sb.WriteString("digraph{\n")
|
||
sb.WriteString("rankdir=\"BT\"\n")
|
||
|
||
for i, v := range states {
|
||
if vs, ok := any(v).(fmt.Stringer); ok {
|
||
fmt.Fprintf(&sb, "n%d [label=%q]\n", i, vs)
|
||
} else {
|
||
fmt.Fprintf(&sb, "n%d [label=%q]\n", i, fmt.Sprintf("%v", v))
|
||
}
|
||
}
|
||
|
||
var l L
|
||
|
||
for dx, x := range states {
|
||
for dy, y := range states {
|
||
if dx == dy {
|
||
continue
|
||
}
|
||
|
||
if l.Equals(l.Merge(x, y), y) {
|
||
fmt.Fprintf(&sb, "n%d -> n%d\n", dx, dy)
|
||
}
|
||
}
|
||
}
|
||
|
||
sb.WriteString("}")
|
||
return sb.String()
|
||
}
|