///|
/// Edge direction is lower extent -> upper extent (subset order).
pub struct Cover {
lower : Int32
upper : Int32
} derive(Eq, Debug)
///|
pub extend Cover with Eq::{equal, not_equal}
///|
pub extend Cover with Debug::{to_repr}
///|
pub struct Lattice {
priv nodes : Array[Concept]
priv edges : Array[Cover]
priv order : Array[Array[Bool]]
} derive(Debug)
///|
pub extend Lattice with Debug::{to_repr}
///|
/// Materialize a bounded Hasse diagram; cubic cover reduction is limited to 256 concepts.
pub fn Context::lattice(
self : Context,
limit~ : Int32 = 256,
) -> Lattice raise FcaError {
if limit < 1 || limit > 256 {
raise Invalid("lattice limit must be 1..256")
}
let nodes = self.concepts(limit~)
let n = nodes.length()
let order : Array[Array[Bool]] = []
for i = 0; i < n; i = i + 1 {
let row : Array[Bool] = []
for j = 0; j < n; j = j + 1 {
row.push(set_subset(nodes[i].extent, nodes[j].extent))
}
order.push(row)
}
let edges : Array[Cover] = []
for i = 0; i < n; i = i + 1 {
for j = 0; j < n; j = j + 1 {
if i != j && order[i][j] {
let mut direct = true
for k = 0; k < n; k = k + 1 {
if k != i && k != j && order[i][k] && order[k][j] {
direct = false
break
}
}
if direct {
edges.push({ lower: i, upper: j, })
}
}
}
}
{ nodes, edges, order, }
}
///|
fn Lattice::check_node(self : Lattice, node : Int32) -> Unit raise FcaError {
if node < 0 || node >= self.nodes.length() {
raise Invalid("lattice node out of range")
}
}
///|
pub fn Lattice::size(self : Lattice) -> Int32 {
self.nodes.length()
}
///|
pub fn Lattice::concept(self : Lattice, node : Int32) -> Concept raise FcaError {
self.check_node(node)
{
extent: self.nodes[node].extent.copy(),
intent: self.nodes[node].intent.copy(),
}
}
///|
pub fn Lattice::covers(self : Lattice) -> Array[Cover] {
self.edges.copy()
}
///|
pub fn Lattice::leq(
self : Lattice,
lower : Int32,
upper : Int32,
) -> Bool raise FcaError {
self.check_node(lower)
self.check_node(upper)
self.order[lower][upper]
}
///|
pub fn Lattice::upper_covers(
self : Lattice,
node : Int32,
) -> Array[Int32] raise FcaError {
self.check_node(node)
let out : Array[Int32] = []
for edge in self.edges {
if edge.lower == node {
out.push(edge.upper)
}
}
out
}
///|
pub fn Lattice::lower_covers(
self : Lattice,
node : Int32,
) -> Array[Int32] raise FcaError {
self.check_node(node)
let out : Array[Int32] = []
for edge in self.edges {
if edge.upper == node {
out.push(edge.lower)
}
}
out
}