Concept.java
package org.thegalactic.lattice;
/*
* Concept.java
*
*
* Copyright: 2010-2015 Karell Bertet, France
* Copyright: 2015-2016 The Galactic Organization, France
*
* License: http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html CeCILL-B license
*
* This file is part of java-lattices.
* You can redistribute it and/or modify it under the terms of the CeCILL-B license.
*/
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import org.thegalactic.context.Context;
import org.thegalactic.dgraph.DAGraph;
import org.thegalactic.dgraph.ConcreteDGraph;
import org.thegalactic.dgraph.Edge;
import org.thegalactic.dgraph.Node;
import org.thegalactic.util.ComparableSet;
/**
* This class gives a representation for a concept, i.e. a node of a concept
* lattice.
*
* A concept extends class {@link Node} by providing two comparable sets defined
* by {@link ComparableSet}, namely `setA` and `setB`, aiming at storing set of
* a concepts.
*
* This component can also be used to store a closed set by using only set `A`.
*
* This class implements class `Comparable` aiming at sorting concepts by
* providing the {@link #compareTo} method. Comparison between this component
* and those in parameter is realised by comparing set `A`.
*
* @todo Should not inherit from Node since content is not used. Maybe by using
* interface.
*
* ![Concept](Concept.png)
*
* @uml Concept.png
* !include resources/org/thegalactic/dgraph/Node.iuml
* !include resources/org/thegalactic/lattice/Concept.iuml
* !include resources/org/thegalactic/util/ComparableSet.iuml
*
* hide members
* show Concept members
* class Concept #LightCyan
* title Concept UML graph
*/
public class Concept extends Node {
/*
* ------------- FIELDS ------------------
*/
/**
* This first set of comparable elements of the concept.
*/
private ComparableSet setA = null;
/**
* This second set of comparable elements of the concept.
*/
private ComparableSet setB = null;
/*
* ------------- CONSTRUCTORS ------------------
*/
/**
* Constructs a new concept containing the specified comparables set as setA
* and setB.
*
* @param setA set of comparable used to initialise setA.
* @param setB set of comparable used to initialise setB.
*/
public Concept(TreeSet<Comparable> setA, TreeSet<Comparable> setB) {
this.setA = new ComparableSet(setA);
this.setB = new ComparableSet(setB);
}
/**
* Constructs a new concept with an empty set of comparableset as setA and
* set B if the two boolean are true. False booleans allow to construct a
* concept with only one of the two sets.
*
* @param setA field setA is empty if true, setA is null if false.
* @param setB field setB is empty if true, setB is null if false.
*/
public Concept(boolean setA, boolean setB) {
if (setA) {
this.setA = new ComparableSet();
}
if (setB) {
this.setB = new ComparableSet();
}
}
/**
* Constructs a new concept containing the specified comparables set as
* setA, and an empty set of comparableset as setB if the boolean is true. A
* false boolean allows to construct a concept with the only set A.
*
* @param setA set of comparable used to initialise setA.
* @param setB field setB is empty if true, setB is null if false.
*/
public Concept(TreeSet<Comparable> setA, boolean setB) {
this.setA = new ComparableSet(setA);
if (setB) {
this.setB = new ComparableSet();
}
}
/**
* Constructs a new concept containing the specified comparables set as
* setB, and an empty set of comparableset as setA if the boolean is true. A
* false boolean allows to construct concept with the only set B.
*
* @param setA field setA is empty if true, setA is null if false.
* @param setB set of comparable used to initialise setB.
*/
public Concept(boolean setA, TreeSet<Comparable> setB) {
this.setB = new ComparableSet(setB);
if (setA) {
this.setA = new ComparableSet();
}
}
/**
* Constructs this component as a copy of the specified ClosedSet.
*
* @param c the closed set to be copied
*/
public Concept(Concept c) {
if (c.hasSetA()) {
this.setA = new ComparableSet(c.getSetA());
} else {
this.setA = null;
}
if (c.hasSetB()) {
this.setB = new ComparableSet(c.getSetB());
} else {
this.setB = null;
}
}
/*
* --------------- ACCESSOR AND OVERLAPPING METHODS ------------
*/
/**
* Returns a clone of this component.
*
* @return a clone of this component
*/
@Override
public Concept clone() {
if (this.hasSetA() && this.hasSetB()) {
TreeSet setA = (TreeSet) this.getSetA().clone();
TreeSet setB = (TreeSet) this.getSetB().clone();
return new Concept(setA, setB);
}
if (this.hasSetA() && !this.hasSetB()) {
TreeSet setA = (TreeSet) this.getSetA().clone();
return new Concept(setA, false);
} else if (this.hasSetB()) {
TreeSet setB = (TreeSet) this.getSetB().clone();
return new Concept(false, setB);
} else {
return new Concept(false, false);
}
}
/**
* Checks if the concept has an empty set B.
*
* @return true if and only if setB is not null
*/
public boolean hasSetB() {
return this.setB != null;
}
/**
* Checks if the concept has an empty set A.
*
* @return true if and only if setA is not null
*/
public boolean hasSetA() {
return this.setA != null;
}
/**
* Returns the set A of this component.
*
* @return the set A of this component
*/
public TreeSet<Comparable> getSetA() {
return this.setA;
}
/**
* Returns the set B of comparable of this component.
*
* @return the set B of this component.
*/
public TreeSet<Comparable> getSetB() {
return this.setB;
}
/**
* Checks if the set A contains the specified comparable.
*
* @param x comparable to find in setA.
*
* @return true if and only if setA contains x.
*/
public boolean containsInA(Comparable x) {
if (this.hasSetA()) {
return this.setA.contains(x);
} else {
return false;
}
}
/**
* Checks if the set B contains the specified comparable.
*
* @param x comparable to find in setB.
*
* @return true if and only if setB contains x.
*/
public boolean containsInB(Comparable x) {
if (this.hasSetB()) {
return this.setB.contains(x);
} else {
return false;
}
}
/**
* Checks if the set A contains the specified set of comparable.
*
* @param x set of comparable to find in setA.
*
* @return true if and only if setA contains all elemens of x.
*/
public boolean containsAllInA(TreeSet x) {
if (this.hasSetA()) {
return this.setA.containsAll(x);
} else {
return false;
}
}
/**
* Checks if the set B contains the specified set of comparable.
*
* @param x set of comparable to find in setB.
*
* @return true if and only if setB contains all elemens of x.
*/
public boolean containsAllInB(TreeSet x) {
if (this.hasSetB()) {
return this.setB.containsAll(x);
} else {
return false;
}
}
/**
* Replaces the set A of this component by the specified one.
*
* @param x set of comparable used to replace setB
*/
public void putSetB(ComparableSet x) {
if (this.hasSetB()) {
this.setB = x;
} else {
this.setB = new ComparableSet(x);
}
}
/**
* Replaces the set A of this component by the specified one.
*
* @param x set of comparable used to replace setA
*/
public void putSetA(ComparableSet x) {
if (this.hasSetA()) {
this.setA = x;
} else {
this.setA = new ComparableSet(x);
}
}
/**
* Adds a comparable to the set A.
*
* @param x comparable to add to setA
*
* @return true if and only if addition is successful.
*/
public boolean addToA(Comparable x) {
if (this.hasSetA()) {
return this.setA.add(x);
} else {
return false;
}
}
/**
* Adds a comparable to the set B.
*
* @param x comparable to add to setB
*
* @return true if and only if addition is successful.
*/
public boolean addToB(Comparable x) {
if (this.hasSetB()) {
return this.setB.add(x);
} else {
return false;
}
}
/**
* Adds the specified set of comparable to the set A.
*
* @param x set of comparable to add to setA
*
* @return true if and only if addition is successful.
*/
public boolean addAllToA(TreeSet x) {
if (this.hasSetA()) {
return this.setA.addAll(x);
} else {
return false;
}
}
/**
* Adds the specified set of comparable to the set B.
*
* @param x set of comparable to add to setB
*
* @return true if and only if addition is successful.
*/
public boolean addAllToB(TreeSet x) {
if (this.hasSetB()) {
return this.setB.addAll(x);
} else {
return false;
}
}
/**
* Remove a comparable from the set A.
*
* @param x comparable to remove from setA
*
* @return true if and only if removal is successful.
*/
public boolean removeFromA(Comparable x) {
if (this.hasSetA()) {
return this.setA.remove(x);
} else {
return false;
}
}
/**
* Remove a comparable from the set B.
*
* @param x comparable to remove from setB
*
* @return true if and only if removal is successful.
*/
public boolean removeFromB(Comparable x) {
if (this.hasSetB()) {
return this.setB.remove(x);
} else {
return false;
}
}
/**
* Remove a set of comparable from the set A.
*
* @param x set to remove from setA
*
* @return true if and only if removal is successful.
*/
public boolean removeAllFromA(TreeSet x) {
if (this.hasSetA()) {
return this.setA.removeAll(x);
} else {
return false;
}
}
/**
* Remove a set of comparable from the set B.
*
* @param x set to remove from setB
*
* @return true if and only if removal is successful.
*/
public boolean removeAllFromB(TreeSet x) {
if (this.hasSetB()) {
return this.setB.removeAll(x);
} else {
return false;
}
}
/*
* --------------- OVERLAPPING METHODS ------------
*/
/**
* Returns the description of this component in a String without spaces.
*
* @return the description of this component in a String without spaces.
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
if (this.hasSetA()) {
builder.append(this.setA);
}
if (this.hasSetA() && this.hasSetB()) {
builder.append('-');
}
if (this.hasSetB()) {
builder.append(this.setB);
}
return builder.toString();
}
/**
* Returns the hash code of this component.
*
* @return hash code of this component
*/
@Override
public int hashCode() {
return super.hashCode();
}
/**
* Compares this component with the specified one.
*
* @param o object compared to this component.
*
* @return true if and only if o is equals to this component.
*/
@Override
public boolean equals(Object o) {
if (!(o instanceof Concept)) {
return false;
}
if (!this.hasSetB()) {
return this.setA.equals(((Concept) o).setA);
}
if (!this.hasSetA()) {
return this.setB.equals(((Concept) o).setB);
}
return this.setA.equals(((Concept) o).setA) && this.setB.equals(((Concept) o).setB);
}
/**
* Compares this component with the specified one sorted by the lectic
* order.
*
* @return a negative integer, zero, or a positive integer as this component
* is less than, equal to, or greater than the specified object.
*/
/*
* public int compareTo(Object o){
* if (!(o instanceof lattice.Concept)) return -1;
* Concept c = (Concept) o;
* //System.out.println("compareTo : "+this+" "+o);
* if (!this.hasSetB()) {
* return this.setA.compareTo(c.setA);
* }
* if (!this.hasSetA()) {
* return this.setB.compareTo(c.setB);
* }
* if (this.setA.compareTo(c.setA)!=0) {
* return this.setB.compareTo(c.setB);
* } else {
* return this.setA.compareTo(c.setA);
* }
* }
*/
/**
* Computes the immediate successors of this component with the LOA
* algorithm.
*
* @param init context from which successor of this component are computed.
*
* @return immediate successors of this component.
*/
public ArrayList<TreeSet<Comparable>> immediateSuccessorsLOA(Context init) {
ArrayList<TreeSet<Comparable>> succB = new ArrayList();
TreeSet<Comparable> attributes = (TreeSet<Comparable>) init.getSet().clone();
attributes.removeAll(this.getSetA());
boolean add;
for (Comparable x : attributes) {
add = true;
Iterator it = succB.iterator();
while (it.hasNext()) {
TreeSet tX = (TreeSet) it.next();
TreeSet<Comparable> bx = (TreeSet<Comparable>) this.getSetA().clone();
bx.add(x);
TreeSet<Comparable> bX = (TreeSet<Comparable>) this.getSetA().clone();
bX.addAll(tX);
TreeSet<Comparable> bXx = (TreeSet<Comparable>) bX.clone();
bXx.add(x);
int cBx = count(init, bx);
int cBX = count(init, bX);
int cBXx = count(init, bXx);
if (cBx == cBX) { // Try to group tests by pairs.
if (cBXx == cBx) {
it.remove(); // Update present potential successor.
TreeSet<Comparable> xX = new TreeSet();
xX.addAll(tX);
xX.add(x);
succB.add(xX);
add = false;
break;
}
}
if (cBx < cBX) {
if (cBXx == cBx) {
add = false;
break;
}
}
if (cBx > cBX) {
if (cBXx == cBX) {
it.remove();
}
}
}
if (add) {
TreeSet<Comparable> t = new TreeSet();
t.add(x);
succB.add(new TreeSet(t));
}
}
for (TreeSet t : succB) {
t.addAll(this.getSetA());
}
return succB;
}
/**
* Returns the number of observations corresponding to the set of attributes
* in the init context.
*
* @param init initial context from which attributes are count.
* @param attributes attributes from which observations are counted.
*
* @return number of observations corresponding to the set of attributes in
* init context.
*/
private int count(Context init, TreeSet<Comparable> attributes) {
return init.getExtentNb(attributes);
}
/**
* Returns the list of immediate successors of a given node of the lattice.
*
* This treatment is an adaptation of Bordat's theorem stating that there is
* a bijection between minimal strongly connected component of the
* precedence subgraph issued from the specified node, and its immediate
* successors.
*
* This treatment is performed in O(Cl|S|^3log g) where S is the initial set
* of elements, Cl is the closure computation complexity and g is the number
* of minimal generators of the lattice.
*
* This treatment is recursively invoked by method recursiveDiagramlattice.
* In this case, the dependance graph is initialised by method
* recursiveDiagramMethod, and updated by this method, with addition some
* news edges and/or new valuations on existing edges. When this treatment
* is not invoked by method recursiveDiagramLattice, then the dependance
* graph is initialised, but it may be not complete. It is the case for
* example for on-line generation of the concept lattice.
*
* cguerin - 2013-04-12 - transfer immedateSuccessors method from
* ConceptLattice to Concept
*
* @param init closure system used to compute immediate successors of this
* component.
*
* @return the list of immediate successors of this component.
*/
public ArrayList<TreeSet<Comparable>> immediateSuccessors(ClosureSystem init) {
// Initialisation of the dependance graph when not initialised by method recursiveDiagramLattice
ConcreteDGraph<Comparable, ?> dependenceGraph = new ConcreteDGraph<Comparable, Object>();
for (Comparable c : init.getSet()) {
dependenceGraph.addNode(new Node(c));
}
// computes newVal, the subset to be used to valuate every new dependance relation
// newVal = F\predecessors of F in the precedence graph of the closure system
// For a non reduced closure system, the precedence graph is not acyclic,
// and therefore strongly connected components have to be used.
ComparableSet f = new ComparableSet(this.getSetA());
long start = System.currentTimeMillis();
System.out.print("Precedence graph... ");
ConcreteDGraph prec = init.precedenceGraph();
System.out.println(System.currentTimeMillis() - start + "ms");
start = System.currentTimeMillis();
System.out.print("Srongly connected component... ");
DAGraph<SortedSet<Node<Comparable>>, ?> acyclPrec = prec.getStronglyConnectedComponent();
System.out.println(System.currentTimeMillis() - start + "ms");
ComparableSet newVal = new ComparableSet();
newVal.addAll(f);
for (Object x : f) {
// computes nx, the strongly connected component containing x
Node<SortedSet<Node<Comparable>>> nx = null;
for (Node cc : acyclPrec.getNodes()) {
TreeSet<Node> cC = (TreeSet<Node>) cc.getContent();
for (Node y : cC) {
if (x.equals(y.getContent())) {
nx = cc;
}
}
}
// computes the minorants of nx in the acyclic graph
SortedSet<Node<SortedSet<Node<Comparable>>>> ccMinNx = acyclPrec.minorants(nx);
// removes from newVal every minorants of nx
for (Node cc : ccMinNx) {
TreeSet<Node> cC = (TreeSet<Node>) cc.getContent();
for (Node y : cC) {
newVal.remove(y.getContent());
}
}
}
// computes the node belonging in S\F
Set<Node<Comparable>> n = new TreeSet<Node<Comparable>>();
for (Node in : dependenceGraph.getNodes()) {
if (!f.contains(in.getContent())) {
n.add(in);
}
}
System.out.print("Dependance... ");
start = System.currentTimeMillis();
// computes the dependance relation between nodes in S\F
// and valuated this relation by the subset of S\F
TreeSet<Edge> e = new TreeSet<Edge>();
for (Node source : n) {
for (Node target : n) {
if (!source.equals(target)) {
// check if source is in dependance relation with target
// i.e. "source" belongs to the closure of "F+target"
ComparableSet fPlusTo = new ComparableSet(f);
fPlusTo.add(target.getContent());
fPlusTo = new ComparableSet(init.closure(fPlusTo));
if (fPlusTo.contains(source.getContent())) {
// there is a dependance relation between source and target
// search for an existing edge between source and target
Edge ed = dependenceGraph.getEdge(source, target);
if (ed == null) {
ed = new Edge(source, target, new TreeSet<ComparableSet>());
dependenceGraph.addEdge(ed);
}
e.add(ed);
// check if F is a minimal set closed for dependance relation between source and target
((TreeSet<ComparableSet>) ed.getContent()).add(newVal);
TreeSet<ComparableSet> valEd = new TreeSet<ComparableSet>((TreeSet<ComparableSet>) ed.getContent());
for (ComparableSet x1 : valEd) {
if (x1.containsAll(newVal) && !newVal.containsAll(x1)) {
((TreeSet<ComparableSet>) ed.getContent()).remove(x1);
}
if (!x1.containsAll(newVal) && newVal.containsAll(x1)) {
((TreeSet<ComparableSet>) ed.getContent()).remove(newVal);
}
}
}
}
}
}
System.out.println(System.currentTimeMillis() - start + "ms");
System.out.print("Subgraph... ");
start = System.currentTimeMillis();
// computes the dependance subgraph of the closed set F as the reduction
// of the dependance graph composed of nodes in S\A and edges of the dependance relation
ConcreteDGraph sub = dependenceGraph.getSubgraphByNodes(n);
ConcreteDGraph delta = sub.getSubgraphByEdges(e);
// computes the sources of the CFC of the dependance subgraph
// that corresponds to successors of the closed set F
DAGraph cfc = delta.getStronglyConnectedComponent();
SortedSet<Node> sccmin = cfc.getSinks();
System.out.println(System.currentTimeMillis() - start + "ms");
ArrayList<TreeSet<Comparable>> immSucc = new ArrayList<TreeSet<Comparable>>();
for (Node n1 : sccmin) {
TreeSet s = new TreeSet(f);
TreeSet<Node> toadd = (TreeSet<Node>) n1.getContent();
for (Node n2 : toadd) {
s.add(n2.getContent());
}
immSucc.add(s);
}
return immSucc;
}
}