Lattice.java

  1. package org.thegalactic.lattice;

  2. /*
  3.  * Lattice.java
  4.  *
  5.  * Copyright: 2010-2015 Karell Bertet, France
  6.  * Copyright: 2015-2016 The Galactic Organization, France
  7.  *
  8.  * License: http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html CeCILL-B license
  9.  *
  10.  * This file is part of java-lattices.
  11.  * You can redistribute it and/or modify it under the terms of the CeCILL-B license.
  12.  */
  13. import org.thegalactic.rule.Rule;
  14. import org.thegalactic.rule.ImplicationalSystem;
  15. import org.thegalactic.rule.AssociationRule;
  16. import java.util.TreeMap;
  17. import java.util.TreeSet;
  18. import java.util.SortedSet;
  19. import java.util.ArrayList;
  20. import java.util.Iterator;
  21. import java.util.LinkedList;

  22. import org.thegalactic.util.ComparableSet;
  23. import org.thegalactic.context.Context;
  24. import org.thegalactic.dgraph.DAGraph;
  25. import org.thegalactic.dgraph.ConcreteDGraph;
  26. import org.thegalactic.dgraph.Edge;
  27. import org.thegalactic.dgraph.Node;

  28. /**
  29.  * This class extends class {@link org.thegalactic.dgraph.DAGraph} to provide
  30.  * specific methods to manipulate a lattice.
  31.  *
  32.  * A lattice is a directed acyclic graph
  33.  * ({@link org.thegalactic.dgraph.DAGraph}) containing particular nodes denoted
  34.  * join and meet\ (a dag is a lattice if and only if each pair of nodes admits a
  35.  * join and a meet).
  36.  *
  37.  * Since checking the lattice property is very time-expensive, this property is
  38.  * not ensured for components of this class. However, it can be explicitely
  39.  * ckecked using method {@link #isLattice}.
  40.  *
  41.  * This class provides methods implementing classical operation on a lattice
  42.  * issued from join and meet operation and irreducibles elements, and methods
  43.  * that returns a condensed representation of a lattice.
  44.  *
  45.  * A well-known condensed representation of a lattice is its table, obtained by
  46.  * method {@link #getTable}, where join-irreducibles are in column and
  47.  * meet-irreducibles are in rown.
  48.  *
  49.  * Another condensed representation is its dependency graph obtained by method
  50.  * {@link #getDependencyGraph}.
  51.  *
  52.  * The dependency graph is a directed graph where nodes are join-irreducibles,
  53.  * edges corresponds to the dependency relation between two join-irreducibles
  54.  * and are valuated by a family of subsets of irreducibles.
  55.  *
  56.  * The dependency graph encodes two other condensed representation of a lattice
  57.  * that are its minimal generators and its canonical direct basis that can be
  58.  * obtained from the dependency graph by methods {@link #getMinimalGenerators}
  59.  * and {@link #getCanonicalDirectBasis}.
  60.  *
  61.  * ![Lattice](Lattice.png)
  62.  *
  63.  * @param <N> Node content type
  64.  * @param <E> Edge content type
  65.  *
  66.  * @todo remove useless comments: Karell
  67.  *
  68.  * @uml Lattice.png
  69.  * !include resources/org/thegalactic/dgraph/DAGraph.iuml
  70.  * !include resources/org/thegalactic/dgraph/DGraph.iuml
  71.  * !include resources/org/thegalactic/dgraph/Edge.iuml
  72.  * !include resources/org/thegalactic/dgraph/Node.iuml
  73.  * !include resources/org/thegalactic/lattice/Lattice.iuml
  74.  *
  75.  * hide members
  76.  * show Lattice members
  77.  * class Lattice #LightCyan
  78.  * title Lattice UML graph
  79.  */
  80. public class Lattice<N, E> extends DAGraph<N, E> {

  81.     /*
  82.      * ------------- FIELDS ------------------
  83.      */
  84.     /**
  85.      * The dependency graph of a lattice.
  86.      *
  87.      * Nodes are join irreducibles elements, and edges correspond to the
  88.      * dependency relation of the lattice (j -> j' if and only if there exists a
  89.      * node x in the lattice such that x not greather than j and j', and x v j'
  90.      * > j), and edges are labeled with the smallest subsets X of
  91.      * join-irreducibles such that the join of elements of X corresponds to the
  92.      * node x of the lattice.
  93.      */
  94.     private ConcreteDGraph dependencyGraph = null;

  95.     /*
  96.      * ------------- CONSTRUCTORS ------------------
  97.      */
  98.     /**
  99.      * Constructs this component with an empty set of nodes.
  100.      */
  101.     public Lattice() {
  102.         super();
  103.     }

  104.     /**
  105.      * Constructs this component with the specified set of nodes, and empty
  106.      * treemap of successors and predecessors.
  107.      *
  108.      * @param set the set of nodes
  109.      */
  110.     public Lattice(SortedSet<Node<N>> set) {
  111.         super(set);
  112.     }

  113.     /**
  114.      * Constructs this component as a copy of the specified lattice.
  115.      *
  116.      * Lattice property is checked for the specified lattice.
  117.      *
  118.      * When not verified, this component is construct with an empty set of
  119.      * nodes.
  120.      *
  121.      * @param graph the Lattice to be copied
  122.      */
  123.     public Lattice(DAGraph<N, E> graph) {
  124.         super(graph);
  125.         if (!this.isAcyclic()) {
  126.             this.setNodes(new TreeSet<Node<N>>());
  127.             this.setSuccessors(new TreeMap<Node<N>, TreeSet<Edge<N, E>>>());
  128.             this.setPredecessors(new TreeMap<Node<N>, TreeSet<Edge<N, E>>>());
  129.         }
  130.     }

  131.     /*
  132.      * ------------- LATTICE CHEKING METHODS ------------------
  133.      */
  134.     /**
  135.      * Check if this component is a lattice.
  136.      *
  137.      * There exists several caracterizations of a lattice. The characterization
  138.      * implemented is the following: A lattice is a DAG if there exists a meet
  139.      * for each pair of node, and a unique maximal node.
  140.      *
  141.      * This treatment is performed in O(n^3), where n is the number of nodes.
  142.      *
  143.      * @return the truth value for this property
  144.      */
  145.     public boolean isLattice() {
  146.         if (!this.isAcyclic()) {
  147.             return false;
  148.         }
  149.         for (Node<N> x : this.getNodes()) {
  150.             for (Node<N> y : this.getNodes()) {
  151.                 if (this.meet(x, y) == null) {
  152.                     return false;
  153.                 }
  154.             }
  155.         }
  156.         return this.max().size() == 1;
  157.     }

  158.     /**
  159.      * Return true if this component is congruence normal.
  160.      *
  161.      * A lattice $L$ is in class CN (Congruence Normal) is there exists a
  162.      * sequence $L_1,\ldots,L_p$ of lattices with $L_p=L$, together with a
  163.      * sequence $C_1,\ldots,C_{p-1}$ such that $C_i$ is a convex of $L_i$ and
  164.      * $L_{i+1}$ is obtained by doubling the convex $C_i$ in $L_i$.
  165.      *
  166.      * See {@link org.thegalactic.lattice.LatticeFactory} for the doubling
  167.      * convex method which is not used here.
  168.      *
  169.      * This computation is done in O((|J|+|M|)^2|X|) from the transitive
  170.      * reduction of L.
  171.      *
  172.      * This recognition algorithm was first written in : "Doubling convex serts
  173.      * in lattices : characterizations and recognition algorithm", Bertet K.,
  174.      * Caspard N. 2002.
  175.      *
  176.      * @return true if this component is congruence normal.
  177.      */
  178.     public boolean isCN() {
  179.         TreeSet<Node<N>> joins = this.joinIrreducibles();
  180.         TreeSet<Node<N>> meets = this.meetIrreducibles();
  181.         ArrowRelation arrows = new ArrowRelation(this);
  182.         Context dbl = arrows.getDoubleArrowTable();
  183.         // steps are connected component of the double arrow table.
  184.         ArrayList<Concept> steps = new ArrayList<Concept>();
  185.         while (!joins.isEmpty()) {
  186.             TreeSet<Comparable> setA = new TreeSet<Comparable>();
  187.             TreeSet<Comparable> setB = new TreeSet<Comparable>();
  188.             int cardA = setA.size();
  189.             setA.add(joins.first());
  190.             while (cardA != setA.size()) { // something added
  191.                 cardA = setA.size(); // update card
  192.                 for (Comparable c : setA) {
  193.                     setB.addAll(dbl.getIntent(c));
  194.                 }
  195.                 for (Comparable c : setB) {
  196.                     setA.addAll(dbl.getExtent(c));
  197.                 }
  198.             }
  199.             steps.add(new Concept(setA, setB));
  200.             joins.removeAll(setA);
  201.         }
  202.         for (Concept c : steps) {
  203.             if (c.getSetB().isEmpty()) { // to be verified :-)
  204.                 return false;
  205.             }
  206.             for (Comparable j : c.getSetA()) {
  207.                 for (Comparable m : c.getSetB()) {
  208.                     if (arrows.getEdge((Node) j, (Node) m).getContent() != "UpDown"
  209.                             && arrows.getEdge((Node) j, (Node) m).getContent() != "Circ") {
  210.                         return false;
  211.                     }
  212.                 }
  213.             }
  214.         }
  215.         joins = this.joinIrreducibles();
  216.         meets = this.meetIrreducibles();
  217.         ConcreteDGraph phi = new ConcreteDGraph();
  218.         for (Node<N> j : joins) {
  219.             for (Node<N> m : meets) {
  220.                 int indJ = 0; // Search for the step containning j
  221.                 while (indJ < steps.size() && !steps.get(indJ).containsInA(j)) {
  222.                     indJ++;
  223.                 }
  224.                 if (phi.getNodeByContent(indJ) == null && indJ != steps.size()) {
  225.                     phi.addNode(new Node(indJ));
  226.                 }
  227.                 int indM = 0; // Search for the step containning m
  228.                 while (indM < steps.size() && !steps.get(indM).containsInB(m)) {
  229.                     indM++;
  230.                 }
  231.                 if (phi.getNodeByContent(indM) == null && indM != steps.size()) {
  232.                     phi.addNode(new Node(indM));
  233.                 }
  234.                 if (indM != steps.size() && indJ != steps.size()) {
  235.                     if (arrows.getEdge((Node) j, (Node) m).getContent() == "Up") {
  236.                         phi.addEdge(phi.getNodeByContent(indM), phi.getNodeByContent(indJ));
  237.                     }
  238.                     if (arrows.getEdge((Node) j, (Node) m).getContent() == "Down") {
  239.                         phi.addEdge(phi.getNodeByContent(indJ), phi.getNodeByContent(indM));
  240.                     }
  241.                 }
  242.             }
  243.         }
  244.         return (phi.isAcyclic());
  245.     }

  246.     /**
  247.      * Returns true if this component is an atomistic lattice.
  248.      *
  249.      * A lattice is atomistic if its join irreductibles are atoms (e.g.
  250.      * successors of bottom)
  251.      *
  252.      * @return true if this component is atomistic.
  253.      */
  254.     public boolean isAtomistic() {
  255.         TreeSet<Node<N>> join = this.joinIrreducibles();
  256.         TreeSet<Node> atoms = new TreeSet<Node>(this.getSuccessorNodes(this.bottom()));
  257.         return join.containsAll(atoms) && atoms.containsAll(join);
  258.     }

  259.     /**
  260.      * Returns true if this component is an coatomistic lattice.
  261.      *
  262.      * A lattice is coatomistic if its mett irreductibles are coatoms (e.g.
  263.      * predecessors of top)
  264.      *
  265.      * @return true if this component is coatomistic.
  266.      */
  267.     public boolean isCoAtomistic() {
  268.         TreeSet<Node<N>> meet = this.meetIrreducibles();
  269.         SortedSet<Node<N>> coatoms = this.getPredecessorNodes(this.top());
  270.         return meet.containsAll(coatoms) && coatoms.containsAll(meet);
  271.     }

  272.     /*
  273.      * --------------- LATTICE HANDLING METHODS ------------
  274.      */
  275.     /**
  276.      * Returns the top of the lattice.
  277.      *
  278.      * @return the node which is at the top of the lattice or null if it is not
  279.      *         unique
  280.      */
  281.     public Node<N> top() {
  282.         TreeSet<Node<N>> max = new TreeSet<Node<N>>(this.max());
  283.         if (max.size() == 1) {
  284.             return max.first();
  285.         }
  286.         return null;
  287.     }

  288.     /**
  289.      * Returns the bottom of the lattice.
  290.      *
  291.      * @return the node which is at the bottom of the lattice or null if it is
  292.      *         not unique
  293.      */
  294.     public Node<N> bottom() {
  295.         TreeSet<Node<N>> min = new TreeSet<Node<N>>(this.min());
  296.         if (min.size() == 1) {
  297.             return min.first();
  298.         }
  299.         return null;
  300.     }

  301.     /**
  302.      * Returns the meet of the two specified nodes if it exists.
  303.      *
  304.      * @param x the first node
  305.      * @param y the second node
  306.      *
  307.      * @return the node which is at the meet of the nodes or null if it does not
  308.      *         exist
  309.      *
  310.      * @todo this.minorants should return an iterator
  311.      */
  312.     public Node<N> meet(Node<N> x, Node<N> y) {
  313.         // TODO minorants should return an iterator
  314.         SortedSet<Node<N>> xMinorants = new TreeSet<Node<N>>(this.minorants(x));
  315.         xMinorants.add(x);

  316.         SortedSet<Node<N>> yMinorants = new TreeSet<Node<N>>(this.minorants(y));
  317.         yMinorants.add(y);

  318.         xMinorants.retainAll(yMinorants);
  319.         DAGraph<N, E> graph = this.getSubgraphByNodes(xMinorants);
  320.         TreeSet<Node> meet = new TreeSet<Node>(graph.max());
  321.         if (meet.size() == 1) {
  322.             return meet.first();
  323.         }
  324.         return null;
  325.     }

  326.     /**
  327.      * Returns the join of the two specified nodes if it exists.
  328.      *
  329.      * @param x the first node
  330.      * @param y the second node
  331.      *
  332.      * @return the node which is at the join of the nodes or null if it does not
  333.      *         exist
  334.      *
  335.      * @todo this.majorants should return an iterator
  336.      */
  337.     public Node<N> join(Node<N> x, Node<N> y) {
  338.         // TODO this.majorants should return an iterator
  339.         SortedSet<Node<N>> xMajorants = new TreeSet<Node<N>>(this.majorants(x));
  340.         xMajorants.add(x);

  341.         SortedSet<Node<N>> yMajorants = new TreeSet<Node<N>>(this.majorants(y));
  342.         yMajorants.add(y);

  343.         xMajorants.retainAll(yMajorants);
  344.         DAGraph<N, E> graph = this.getSubgraphByNodes(xMajorants);
  345.         TreeSet<Node> join = new TreeSet<Node>(graph.min());
  346.         if (join.size() == 1) {
  347.             return join.first();
  348.         }
  349.         return null;
  350.     }

  351.     /*
  352.      * ------------- IRREDUCIBLES RELATIVE METHODS ------------------
  353.      */
  354.     /**
  355.      * Returns the set of join irreducibles of this component.
  356.      *
  357.      * Join irreducibles are nodes with an unique immediate predecessor in the
  358.      * transitive and reflexive reduction. This component is first reduced
  359.      * reflexively and transitively.
  360.      *
  361.      * @return the set of join irreducibles of this component
  362.      */
  363.     public TreeSet<Node<N>> joinIrreducibles() {
  364.         DAGraph<N, E> graph = new DAGraph(this);
  365.         graph.reflexiveReduction();
  366.         graph.transitiveReduction();
  367.         TreeSet<Node<N>> set = new TreeSet();
  368.         for (Node<N> node : graph.getNodes()) {
  369.             if (graph.getPredecessorNodes(node).size() == 1) {
  370.                 set.add(node);
  371.             }
  372.         }
  373.         return set;
  374.     }

  375.     /**
  376.      * Returns the set of meet irreducibles of this component.
  377.      *
  378.      * Meet irreducibles are nodes with an unique immediate successor in the
  379.      * transitive and reflexiv reduction. This component is first reduced
  380.      * reflexively and transitively.
  381.      *
  382.      * @return the set of meet irreducibles of this component.
  383.      */
  384.     public TreeSet<Node<N>> meetIrreducibles() {
  385.         DAGraph<N, E> graph = new DAGraph(this);
  386.         graph.reflexiveReduction();
  387.         graph.transitiveReduction();
  388.         TreeSet<Node<N>> set = new TreeSet();
  389.         for (Node<N> node : graph.getNodes()) {
  390.             if (graph.getSuccessorNodes(node).size() == 1) {
  391.                 set.add(node);
  392.             }
  393.         }
  394.         return set;
  395.     }

  396.     /**
  397.      * Returns the set of join-irreducibles that are minorants of the specified
  398.      * node.
  399.      *
  400.      * @param node a specified node
  401.      *
  402.      * @return the set of join-irreducibles thar are minorants of the specified
  403.      *         node
  404.      */
  405.     public TreeSet<Node<N>> joinIrreducibles(Node<N> node) {
  406.         TreeSet<Node<N>> join = new TreeSet<Node<N>>(this.joinIrreducibles());
  407.         TreeSet<Node<N>> min = new TreeSet<Node<N>>(this.minorants(node));
  408.         min.add(node);
  409.         min.retainAll(join);
  410.         return min;
  411.     }

  412.     /**
  413.      * Returns the set of meet-irreducibles thar are majorants of the specified
  414.      * node.
  415.      *
  416.      * @param node a specified node
  417.      *
  418.      * @return the set of meet-irreducibles thar are majorants of the specified
  419.      *         node
  420.      */
  421.     public TreeSet<Node<N>> meetIrreducibles(Node<N> node) {
  422.         TreeSet<Node<N>> meet = new TreeSet<Node<N>>(this.meetIrreducibles());
  423.         TreeSet<Node<N>> maj = new TreeSet<Node<N>>(this.majorants(node));
  424.         maj.retainAll(meet);
  425.         return maj;
  426.     }

  427.     /**
  428.      * Returns the subgraph induced by the join irreducibles nodes of this
  429.      * component.
  430.      *
  431.      * @return the subgraph induced by the join irreducibles nodes of this
  432.      *         component
  433.      */
  434.     public DAGraph<N, E> joinIrreduciblesSubgraph() {
  435.         TreeSet<Node<N>> irr = this.joinIrreducibles();
  436.         return this.getSubgraphByNodes(irr);
  437.     }

  438.     /**
  439.      * Returns the subgraph induced by the meet irreducibles nodes of this
  440.      * component.
  441.      *
  442.      * @return the subgraph induced by the meet irreducibles nodes of this
  443.      *         component
  444.      */
  445.     public DAGraph<N, E> meetIrreduciblesSubgraph() {
  446.         TreeSet<Node<N>> irr = this.meetIrreducibles();
  447.         return this.getSubgraphByNodes(irr);
  448.     }

  449.     /**
  450.      * Returns the subgraph induced by the irreducibles nodes of this component.
  451.      *
  452.      * @return the subgraph induced by the irreducibles nodes of this component
  453.      */
  454.     public DAGraph<N, E> irreduciblesSubgraph() {
  455.         TreeSet<Node<N>> irr = this.meetIrreducibles();
  456.         irr.addAll(this.joinIrreducibles());
  457.         return this.getSubgraphByNodes(irr);
  458.     }

  459.     /**
  460.      * Generates and returns the isomorphic closed set lattice defined on the
  461.      * join irreducibles set.
  462.      *
  463.      * Each node of this component is replaced by a node containing its join
  464.      * irreducibles predecessors stored in a closed set.
  465.      *
  466.      * @return the isomorphic closed set lattice defined on the join
  467.      *         irreducibles set
  468.      */
  469.     public ConceptLattice joinClosure() {
  470.         ConceptLattice csl = new ConceptLattice();
  471.         // associates each node to a new closed set of join irreducibles
  472.         TreeSet<Node<N>> join = this.joinIrreducibles();
  473.         TreeMap<Node, Concept> closure = new TreeMap<Node, Concept>();
  474.         Lattice<N, E> lattice = new Lattice(this);
  475.         lattice.transitiveClosure();
  476.         lattice.reflexiveClosure();
  477.         for (Node<N> target : lattice.getNodes()) {
  478.             ComparableSet jx = new ComparableSet();
  479.             for (Node<N> source : lattice.getPredecessorNodes(target)) {
  480.                 if (join.contains(source)) {
  481.                     jx.add(source.getContent());
  482.                 }
  483.             }
  484.             closure.put(target, new Concept(jx, false));
  485.         }
  486.         // addition of nodes
  487.         for (Node<N> node : this.getNodes()) {
  488.             csl.addNode(closure.get(node));
  489.         }
  490.         // addition of edges
  491.         for (Edge ed : this.getEdges()) {
  492.             csl.addEdge(closure.get(ed.getSource()), closure.get(ed.getTarget()));
  493.         }
  494.         return csl;
  495.     }

  496.     /**
  497.      * Generates and returns the isomorphic closed set lattice defined on the
  498.      * meet irreducibles set.
  499.      *
  500.      * Each node of this component is replaced by a node containing its meet
  501.      * irreducibles successors stored in a closed set.
  502.      *
  503.      * @return the isomorphic closed set lattice defined on the meet
  504.      *         irreducibles set
  505.      */
  506.     public ConceptLattice meetClosure() {
  507.         ConceptLattice csl = new ConceptLattice();
  508.         // associates each node to a new closed set of join irreducibles
  509.         TreeSet<Node<N>> meet = this.meetIrreducibles();
  510.         TreeMap<Node, Concept> closure = new TreeMap<Node, Concept>();
  511.         Lattice<N, E> lattice = new Lattice(this);
  512.         lattice.transitiveClosure();
  513.         lattice.reflexiveClosure();
  514.         for (Node<N> target : lattice.getNodes()) {
  515.             ComparableSet mx = new ComparableSet();
  516.             for (Node<N> source : lattice.getSuccessorNodes(target)) {
  517.                 if (meet.contains(source)) {
  518.                     mx.add(source);
  519.                 }
  520.             }
  521.             closure.put(target, new Concept(false, mx));
  522.         }
  523.         // addition of nodes
  524.         for (Node node : this.getNodes()) {
  525.             csl.addNode(closure.get(node));
  526.         }
  527.         // addition of edges
  528.         for (Edge ed : this.getEdges()) {
  529.             csl.addEdge(closure.get(ed.getSource()), closure.get(ed.getTarget()));
  530.         }
  531.         return csl;
  532.     }

  533.     /**
  534.      * Generates and returns the isomorphic concept lattice defined on the join
  535.      * and meet irreducibles sets.
  536.      *
  537.      * Each node of this component is replaced by a node containing its meet
  538.      * irreducibles successors and its join irreducibles predecessors stored in
  539.      * a concept.
  540.      *
  541.      * @return the irreducible closure
  542.      */
  543.     public ConceptLattice irreducibleClosure() {
  544.         ConceptLattice conceptLatice = new ConceptLattice();
  545.         // associates each node to a new closed set of join irreducibles
  546.         TreeSet<Node<N>> meet = this.meetIrreducibles();
  547.         TreeSet<Node<N>> join = this.joinIrreducibles();
  548.         TreeMap<Node, Concept> closure = new TreeMap<Node, Concept>();
  549.         Lattice<N, E> lattice = new Lattice(this);
  550.         lattice.transitiveClosure();
  551.         lattice.reflexiveClosure();
  552.         for (Node<N> target : lattice.getNodes()) {
  553.             ComparableSet jx = new ComparableSet();
  554.             for (Node<N> source : lattice.getPredecessorNodes(target)) {
  555.                 if (join.contains(source)) {
  556.                     jx.add(source);
  557.                 }
  558.             }
  559.             ComparableSet mx = new ComparableSet();
  560.             for (Node source : lattice.getSuccessorNodes(target)) {
  561.                 if (meet.contains(source)) {
  562.                     mx.add(source);
  563.                 }
  564.             }
  565.             closure.put(target, new Concept(jx, mx));
  566.         }
  567.         // addition of nodes
  568.         for (Node node : this.getNodes()) {
  569.             conceptLatice.addNode(closure.get(node));
  570.         }
  571.         // addition of edges
  572.         for (Edge ed : this.getEdges()) {
  573.             conceptLatice.addEdge(closure.get(ed.getSource()), closure.get(ed.getTarget()));
  574.         }
  575.         return conceptLatice;
  576.     }

  577.     /**
  578.      * Returns the smallest set of nodes of this component containing S such
  579.      * that if a,b in JS then join(a,b) in JS.
  580.      *
  581.      * @param s set of nodes to be closed
  582.      *
  583.      * @return (JS) join-closure of s
  584.      */
  585.     public ComparableSet joinClosure(ComparableSet s) {
  586.         // Algorithm is true because join is idempotent & commutative
  587.         ComparableSet stack = new ComparableSet();
  588.         stack.addAll(s); // Nodes to be explored
  589.         ComparableSet result = new ComparableSet();
  590.         while (!stack.isEmpty()) {
  591.             Node c = (Node) stack.first();
  592.             stack.remove(c);
  593.             result.add(c);
  594.             Iterator<Node> it = stack.iterator();
  595.             ComparableSet newNodes = new ComparableSet(); // Node to be added. Must be done AFTER while
  596.             while (it.hasNext()) {
  597.                 Node node = this.join(it.next(), c);
  598.                 if (!result.contains(node) && !stack.contains(node)) {
  599.                     newNodes.add(node);
  600.                 }
  601.             }
  602.             stack.addAll(newNodes);
  603.         }
  604.         return result;
  605.     }

  606.     /**
  607.      * Returns the smallest set of nodes of this component containing S such
  608.      * that if a,b in MS then meet(a,b) in MS.
  609.      *
  610.      * @param s set of nodes to be closed
  611.      *
  612.      * @return (MS) meet-closure of s
  613.      */
  614.     public ComparableSet meetClosure(ComparableSet s) {
  615.         // Algorithm is true because meet is idempotent & commutative
  616.         ComparableSet stack = new ComparableSet();
  617.         stack.addAll(s); // Nodes to be explored
  618.         ComparableSet result = new ComparableSet();
  619.         while (!stack.isEmpty()) {
  620.             Node c = (Node) stack.first();
  621.             stack.remove(c);
  622.             result.add(c);
  623.             Iterator<Node> it = stack.iterator();
  624.             ComparableSet newNodes = new ComparableSet(); // Node to be added. Must be done AFTER while
  625.             while (it.hasNext()) {
  626.                 Node node = this.meet(it.next(), c);
  627.                 if (!result.contains(node) && !stack.contains(node)) {
  628.                     newNodes.add(node);
  629.                 }
  630.             }
  631.             stack.addAll(newNodes);
  632.         }
  633.         return result;
  634.     }

  635.     /**
  636.      * Returns the smallest sublattice of this component containing s.
  637.      *
  638.      * @param s set of nodes to be closed.
  639.      *
  640.      * @return the smallest sublattice of this component containing s.
  641.      */
  642.     public ComparableSet fullClosure(ComparableSet s) {
  643.         ComparableSet result = new ComparableSet();
  644.         result.addAll(s);
  645.         int present = result.size();
  646.         int previous = 0;
  647.         while (previous != present) {
  648.             previous = present;
  649.             result = this.joinClosure(result);
  650.             result = this.meetClosure(result);
  651.             present = result.size();
  652.         }
  653.         return result;
  654.     }

  655.     /**
  656.      * Returns the list of all sets of nodes that generates all nodes. Both join
  657.      * and meet operations are allowed and the sets are minimal for inclusion.
  658.      *
  659.      * @return : List of all hybridGenerators families.
  660.      */
  661.     public TreeSet<ComparableSet> hybridGenerators() {
  662.         TreeSet<Node<N>> joinIrr = this.joinIrreducibles();
  663.         TreeSet<Node<N>> meetIrr = this.meetIrreducibles();
  664.         ComparableSet bothIrr = new ComparableSet();
  665.         for (Node<N> node : joinIrr) {
  666.             if (meetIrr.contains(node)) {
  667.                 bothIrr.add(node);
  668.             }
  669.         } // bothIrr contains nodes that are join and meet irreductibles.

  670.         TreeSet<ComparableSet> generators = new TreeSet<ComparableSet>();
  671.         // First point is that all minimal families have the same number of nodes.
  672.         LinkedList<ComparableSet> list = new LinkedList<ComparableSet>(); // Family of sets to be examined
  673.         list.add(bothIrr);
  674.         while (!list.isEmpty()) {
  675.             int test;
  676.             if (generators.isEmpty()) {
  677.                 test = this.sizeNodes();
  678.             } else {
  679.                 test = generators.first().size();
  680.             }
  681.             if (test < list.peek().size()) {
  682.                 // Elements are sorted by size, thus if the first is to large, all are.
  683.                 list.clear();
  684.             } else {
  685.                 ComparableSet family = list.poll(); // Retrieve and remove head.
  686.                 if (this.fullClosure(family).size() == this.sizeNodes()) {
  687.                     // This family generates l
  688.                     generators.add(family);
  689.                 } else {
  690.                     for (Node node : this.getNodes()) {
  691.                         ComparableSet newFamily = new ComparableSet();
  692.                         newFamily.addAll(family);
  693.                         newFamily.add(node);
  694.                         if (!list.contains(newFamily)) {
  695.                             list.add(newFamily); // add at the end, bigger families
  696.                         }
  697.                     }
  698.                 }
  699.             }
  700.         }
  701.         return generators;
  702.     }

  703.     /**
  704.      * Returns the table of the lattice, composed of the join and meet
  705.      * irreducibles nodes.
  706.      *
  707.      * Each attribute of the table is a copy of a join irreducibles node. Each
  708.      * observation of the table is a copy of a meet irreducibles node. An
  709.      * attribute is extent of an observation when its join irreducible node is
  710.      * greater than the meet irreducible node in the lattice.
  711.      *
  712.      * @return the table of the lattice
  713.      */
  714.     public Context getTable() {
  715.         // generation of attributes
  716.         TreeSet<Node<N>> join = this.joinIrreducibles();
  717.         //TreeMap<Node,Node> JoinContent = new TreeMap();
  718.         Context context = new Context();
  719.         for (Node<N> j : join) {
  720.             //  Node<N> nj = new Node(j);
  721.             //JoinContent.put(j,nj);
  722.             context.addToAttributes(j);
  723.         }
  724.         // generation of observations
  725.         TreeSet<Node<N>> meet = this.meetIrreducibles();
  726.         //TreeMap<Node,Node> MeetContent = new TreeMap();
  727.         for (Node<N> m : meet) {
  728.             //    Node nm = new Node(m);
  729.             context.addToObservations(m);
  730.             //    MeetContent.put(m,nm);
  731.         }
  732.         // generation of extent-intent
  733.         Lattice tmp = new Lattice(this);
  734.         tmp.transitiveClosure();
  735.         for (Node j : join) {
  736.             for (Node m : meet) {
  737.                 if (j.equals(m) || tmp.getSuccessorNodes(j).contains(m)) {
  738.                     context.addExtentIntent(m, j);
  739.                     //T.addExtentIntent(MeetContent.get(m),JoinContent.get(j));
  740.                 }
  741.             }
  742.         }
  743.         return context;
  744.     }

  745.     /**
  746.      * Returns an ImplicationalSystem of the lattice defined on the join
  747.      * irreducibles nodes.
  748.      *
  749.      * Each element of the ImplicationalSystem is a copy of a join irreducible
  750.      * node.
  751.      *
  752.      * @return an implicational system
  753.      */
  754.     public ImplicationalSystem getImplicationalSystem() {
  755.         // initialisation of ImplicationalSystem
  756.         TreeSet<Node<N>> join = this.joinIrreducibles();
  757.         ImplicationalSystem sigma = new ImplicationalSystem();
  758.         for (Node<N> j : join) {
  759.             sigma.addElement((Comparable) j.getContent());
  760.         }
  761.         // generation of the family of closures
  762.         TreeSet<ComparableSet> family = new TreeSet<ComparableSet>();
  763.         Lattice lattice = new Lattice(this);
  764.         ConceptLattice conceptLattice = lattice.joinClosure();
  765.         for (Object node : conceptLattice.getNodes()) {
  766.             Concept concept = (Concept) node;
  767.             ComparableSet setA = new ComparableSet(concept.getSetA());
  768.             family.add(setA);
  769.         }
  770.         // rules generation
  771.         for (ComparableSet jx : family) {
  772.             for (Node j : join) {
  773.                 ComparableSet p = new ComparableSet();
  774.                 p.add(j.getContent());
  775.                 p.addAll(jx);
  776.                 if (!family.contains(p)) {
  777.                     ComparableSet min = new ComparableSet();
  778.                     min.addAll(family.last());
  779.                     for (ComparableSet c : family) {
  780.                         //System.out.println("min: "+min.getClass()+" -C:"+C.getClass());
  781.                         if (c.containsAll(p) && !p.containsAll(c) && min.containsAll(c)) {
  782.                             min = c.clone();
  783.                         }
  784.                     }
  785.                     Rule r = new Rule();
  786.                     r.addAllToPremise(p);
  787.                     min.removeAll(p);
  788.                     r.addAllToConclusion(min);
  789.                     sigma.addRule(r);
  790.                 }
  791.             }
  792.         }

  793.         /**
  794.          * for (Node j : join) for (Node m : meet) if (j.equals(m) ||
  795.          * tmp.getSuccessorNodes(j).contains(m)) T.addExtentIntent (m,j);
  796.          * //T.addExtentIntent (MeetContent.get(m),JoinContent.get(j)); return
  797.          * T;*
  798.          */
  799.         sigma.makeRightMaximal();
  800.         return sigma;
  801.     }

  802.     /*
  803.      * ------------- dependency GRAPH RELATIVE METHODS ------------------
  804.      */
  805.     /**
  806.      * Returns the dependency graph of this component.
  807.      *
  808.      * The dependency graph is a condensed representation of a lattice that
  809.      * encodes its minimal generators, and its canonical direct basis.
  810.      *
  811.      * In the dependency graph, nodes are join irreducibles, egdes correspond to
  812.      * the dependency relation between join-irreducibles (j -> j' if and only if
  813.      * there exists a node x in the lattice such that x not greather than j and
  814.      * j', and x v j' > j), and edges are labeled with the smallest subsets X of
  815.      * join-irreducibles such that the join of elements of X corresponds to the
  816.      * node x of the lattice.
  817.      *
  818.      * The dependency graph could has been already computed in the case where
  819.      * this component has been instancied as the diagramm of the closed set
  820.      * lattice of a closure system using the static method
  821.      * {@link ConceptLattice#diagramLattice} This method implements an
  822.      * adaptation adaptation of Bordat's where the dependency graph is computed
  823.      * while the lattice is generated.
  824.      *
  825.      * However, it is generated in O(nj^3) where n is the number of nodes of the
  826.      * lattice, and j is the number of join-irreducibles of the lattice.
  827.      *
  828.      * @return the dependency graph
  829.      */
  830.     public ConcreteDGraph getDependencyGraph() {
  831.         if (!(this.dependencyGraph == null)) {
  832.             return this.dependencyGraph;
  833.         }
  834.         this.dependencyGraph = new ConcreteDGraph();
  835.         // nodes of the dependency graph are join-irreducibles
  836.         TreeSet<Node<N>> joins = this.joinIrreducibles();
  837.         for (Node<N> j : joins) {
  838.             this.dependencyGraph.addNode(j);
  839.         }
  840.         // computes the transitive closure of the join-irreducibles subgraph of this compnent
  841.         DAGraph joinG = this.irreduciblesSubgraph();
  842.         joinG.transitiveClosure();
  843.         // edges of the dependency graph are dependency relation between join-irreducibles
  844.         // they are first valuated by nodes of the lattice
  845.         for (Node<N> j1 : joins) {
  846.             SortedSet<Node<N>> majj1 = this.majorants(j1);
  847.             for (Node<N> j2 : joins) {
  848.                 if (!j1.equals(j2)) {
  849.                     // computes the set S of nodes not greather than j1 and j2
  850.                     TreeSet<Node<N>> set = new TreeSet<Node<N>>(this.getNodes());
  851.                     set.removeAll(majj1);
  852.                     set.removeAll(this.majorants(j2));
  853.                     set.remove(j1);
  854.                     set.remove(j2);
  855.                     for (Node<N> x : set) {
  856.                         // when j2 V x greather than j1 then add a new edge from j1 to J2
  857.                         // or only a new valuation when the edge already exists
  858.                         if (majj1.contains(this.join(j2, x))) {
  859.                             Edge ed = this.dependencyGraph.getEdge(j1, j2);
  860.                             if (ed == null) {
  861.                                 ed = new Edge(j1, j2, new TreeSet<ComparableSet>());
  862.                                 this.dependencyGraph.addEdge(ed);
  863.                             }
  864.                             // add {Jx minus predecessors in joinG of j in Jx} as valuation of edge
  865.                             // from j1 to j2
  866.                             TreeSet<ComparableSet> valEd = (TreeSet<ComparableSet>) ed.getContent();
  867.                             ComparableSet newValByNode = new ComparableSet(this.joinIrreducibles(x));
  868.                             for (Node<N> j : this.joinIrreducibles(x)) {
  869.                                 newValByNode.removeAll(joinG.getPredecessorNodes(j));
  870.                             }
  871.                             ComparableSet newVal = new ComparableSet();
  872.                             for (Object j : newValByNode) {
  873.                                 Node node = (Node) j;
  874.                                 newVal.add(node.getContent());
  875.                             }
  876.                             ((TreeSet<ComparableSet>) ed.getContent()).add(newVal);
  877.                             // Minimalisation by inclusion of valuations on edge j1->j2
  878.                             valEd = new TreeSet<ComparableSet>((TreeSet<ComparableSet>) ed.getContent());
  879.                             for (ComparableSet x1 : valEd) {
  880.                                 if (x1.containsAll(newVal) && !newVal.containsAll(x1)) {
  881.                                     ((TreeSet<ComparableSet>) ed.getContent()).remove(x1);
  882.                                 }
  883.                                 if (!x1.containsAll(newVal) && newVal.containsAll(x1)) {
  884.                                     ((TreeSet<ComparableSet>) ed.getContent()).remove(newVal);
  885.                                 }
  886.                             }
  887.                         }
  888.                     }
  889.                 }
  890.             }
  891.         }
  892.         // minimalisation of edge's content to get only inclusion-minimal valuation for each edge
  893.         /**
  894.          * for (Edge ed : this.dependencyGraph.getEdges()) {
  895.          * TreeSet<ComparableSet> valEd = new
  896.          * TreeSet<ComparableSet>(((TreeSet<ComparableSet>)ed.getContent()));
  897.          * for (ComparableSet X1 : valEd) for (ComparableSet X2 : valEd) if
  898.          * (X1.containsAll(X2) && !X2.containsAll(X1))
  899.          * ((TreeSet<ComparableSet>)ed.getContent()).remove(X1); }*
  900.          */
  901.         return this.dependencyGraph;
  902.     }

  903.     /**
  904.      * Set the dependency graph.
  905.      *
  906.      * @param graph the dependency graph
  907.      *
  908.      * @return this for chaining
  909.      */
  910.     protected Lattice setDependencyGraph(ConcreteDGraph graph) {
  911.         this.dependencyGraph = graph;
  912.         return this;
  913.     }

  914.     /**
  915.      * Test if this component has a dependency graph.
  916.      *
  917.      * @return the truth value for this property
  918.      */
  919.     protected boolean hasDependencyGraph() {
  920.         return this.dependencyGraph != null;
  921.     }

  922.     /**
  923.      * Returns the canonical direct basis of the lattice.
  924.      *
  925.      * The canonical direct basis is a condensed representation of a lattice
  926.      * encoding by the dependency graph.
  927.      *
  928.      * This canonical direct basis is deduced from the dependency graph of the
  929.      * lattice: for each edge b -> a valuated by a subset X, the rule a+X->b is
  930.      * a rule of the canonical direct basis.
  931.      *
  932.      * If not yet exists, the dependency graph of this component has to be
  933.      * generated by method {@link #getDependencyGraph}.
  934.      *
  935.      * @return the canonical direct basis of the lattice
  936.      */
  937.     public ImplicationalSystem getCanonicalDirectBasis() {
  938.         ConcreteDGraph odGraph = this.getDependencyGraph();
  939.         // initialise elements of the ImplicationalSystem with nodes of the ODGraph
  940.         ImplicationalSystem bcd = new ImplicationalSystem();
  941.         for (Object node : odGraph.getNodes()) {
  942.             bcd.addElement((Comparable) ((Node) node).getContent());
  943.         }
  944.         // computes rules of the BCD from edges of the ODGraph
  945.         for (Object ed : odGraph.getEdges()) {
  946.             Node source = ((Edge) ed).getSource();
  947.             Node target = ((Edge) ed).getTarget();
  948.             TreeSet<ComparableSet> l = (TreeSet<ComparableSet>) ((Edge) ed).getContent();
  949.             for (ComparableSet set : l) {
  950.                 ComparableSet premise = new ComparableSet(set);
  951.                 premise.add((Comparable) target.getContent());
  952.                 ComparableSet conclusion = new ComparableSet();
  953.                 conclusion.add((Comparable) source.getContent());
  954.                 bcd.addRule(new Rule(premise, conclusion));
  955.             }
  956.         }
  957.         //bcd.makeLeftMinimal();
  958.         bcd.makeCompact();
  959.         return bcd;
  960.     }

  961.     /**
  962.      * Returns a set of association rules based on a confidence threshold.
  963.      *
  964.      * The canonical direct basis is computed. For each generated rule, a set of
  965.      * approximative rules (above the confidence threshold) is generated.
  966.      *
  967.      * @param context    a context
  968.      * @param support    a support threshold, between 0 and 1
  969.      * @param confidence a confidence threshold, between 0 and 1
  970.      *
  971.      * @return a set of approximative rules
  972.      */
  973.     public ImplicationalSystem getAssociationBasis(Context context, double support, double confidence) {

  974.         //nb of observations in current context
  975.         int nbObs = context.getObservations().size();

  976.         //start by getting exact rules
  977.         ImplicationalSystem exactRules = this.getCanonicalDirectBasis();
  978.         ImplicationalSystem appRules = new ImplicationalSystem();

  979.         //copy elements from exact rules to approximate rules
  980.         for (Comparable e : exactRules.getSet()) {
  981.             appRules.addElement(e);
  982.         }

  983.         for (Rule rule : exactRules.getRules()) {

  984.             //we get the premisse of the rule, aka the closed set minimal generator
  985.             TreeSet<Comparable> gm = rule.getPremise();
  986.             //then we retrieve the closed set from the minimal generator
  987.             TreeSet<Comparable> closedSet = context.closure(gm);

  988.             //we get the cardinality of its extent to compute confidence later
  989.             double supportClosedSet = context.getExtentNb(closedSet);
  990.             if (supportClosedSet / nbObs > support) {
  991.                 //we get the immediate successors of the concept made of the set
  992.                 ArrayList<TreeSet<Comparable>> succs = new Concept(closedSet, new TreeSet()).immediateSuccessorsLOA(context);
  993.                 for (TreeSet<Comparable> succ : succs) {
  994.                     //we compute the support of the rule as the ratio between closed set and successor extent
  995.                     double ex = context.getExtentNb(succ);
  996.                     double supportSucc = ex / supportClosedSet;

  997.                     //the rule conclusion is made of the successors minus the minimal generator
  998.                     TreeSet<Comparable> conclusions = new TreeSet(succ);
  999.                     conclusions.removeAll(gm);

  1000.                     //if the ratio support exceed the confidence threshold, the rule is created
  1001.                     if (supportSucc > confidence) {
  1002.                         appRules.addRule(new AssociationRule(gm, conclusions, ex / nbObs, supportSucc));
  1003.                     }
  1004.                 }
  1005.                 //the exact rule is copied in the output rule set
  1006.                 appRules.addRule(new AssociationRule(rule.getPremise(), rule.getConclusion(), supportClosedSet / nbObs, 1));
  1007.             }
  1008.         }
  1009.         appRules.makeCompactAssociation();
  1010.         return appRules;
  1011.     }

  1012.     /**
  1013.      * Returns the minimal generators of the lattice.
  1014.      *
  1015.      * Minimal generators a condensed representation of a lattice encoding by
  1016.      * the dependency graph.
  1017.      *
  1018.      * Minimal generators are premises of the canonical direct basis. that is
  1019.      * deduced from the dependency graph of the lattice.
  1020.      *
  1021.      * If not yet exists, the dependency graph of this component has to be
  1022.      * generated by method {@link #getDependencyGraph}.
  1023.      *
  1024.      * @return a TreeSet of the minimal generators
  1025.      */
  1026.     public TreeSet getMinimalGenerators() {
  1027.         ImplicationalSystem bcd = this.getCanonicalDirectBasis();
  1028.         TreeSet genMin = new TreeSet();
  1029.         for (Rule r : bcd.getRules()) {
  1030.             genMin.add(r.getPremise());
  1031.         }
  1032.         return genMin;
  1033.     }

  1034.     /**
  1035.      * The arrowRelation method encodes arrow relations between meet &
  1036.      * join-irreductibles of this component.
  1037.      *
  1038.      * Let m and j be respectively meet and join irreductibles of a lattice.
  1039.      * Recall that m has a unique successor say m+ and j has a unique
  1040.      * predecessor say j-, then :
  1041.      *
  1042.      * - j "Up Arrow" m (stored has "Up") iff j is not less or equal than m and j is less than m+
  1043.      * - j "Down Arrow" m (stored has "Down") iff j is not less or equal than m and j- is less than m
  1044.      * - j "Up Down Arrow" m (stored has "UpDown") iff j "Up" m and j "Down" m
  1045.      * - j "Cross" m (stored has "Cross") iff j is less or equal than m
  1046.      * - j "Circ" m (stored has "Circ") iff neither j "Up" m nor j "Down" m nor j "Cross" m
  1047.      *
  1048.      * @return ConcreteDGraph whose :
  1049.          - Nodes are join or meet irreductibles of the lattice.
  1050.          - Edges content encodes arrows as String "Up", "Down", "UpDown", "Cross", "Circ".
  1051.      *
  1052.      */
  1053.     public ArrowRelation getArrowRelation() {
  1054.         return new ArrowRelation(this);
  1055.     }
  1056. }