LatticeFactory.java
package org.thegalactic.lattice;
/*
* LatticeFactory.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.BitSet;
import java.util.Iterator;
import org.thegalactic.dgraph.DAGraph;
import org.thegalactic.dgraph.DAGraphFactory;
import org.thegalactic.dgraph.Node;
import org.thegalactic.util.Couple;
/**
* This class provides a few methods to constructs lattice examples.
*
* ![LatticeFactory](LatticeFactory.png)
*
* @uml LatticeFactory.png
* !include resources/org/thegalactic/lattice/LatticeFactory.iuml
*
* class LatticeFactory #LightCyan
* title LatticeFactory UML graph
* @author jeff
*/
public class LatticeFactory {
/**
* Returns a randomly generated lattice with nb nodes.
*
* @param nb Number of nodes in the randomly generated lattice
*
* @return a randomly generated lattice with nb nodes
*/
public static Lattice<Integer, ?> random(int nb) {
boolean done = false;
Lattice l = new Lattice();
while (!done) {
DAGraph dag = DAGraphFactory.getInstance().random(nb - 2); // what an ugly strategy :-(
Lattice<Integer, ?> tmp = new Lattice(dag);
Node<Integer> top = new Node(nb - 1);
tmp.addNode(top);
for (Node<Integer> node : tmp.max()) {
if (!node.equals(top)) {
tmp.addEdge(node, top);
}
}
Node<Integer> bot = new Node(nb);
tmp.addNode(bot);
for (Node<Integer> node : tmp.min()) {
if (!node.equals(bot)) {
tmp.addEdge(bot, node);
}
}
if (tmp.isLattice()) {
done = true;
l = tmp;
}
}
return l;
}
/**
* Returns the boolean algebra of cardinal 2^n.
*
* @param n cardinal of the boolean algebra return by this method is 2^n
*
* @return the boolean algebra of cardinal 2^n
*/
public static Lattice booleanAlgebra(int n) {
Lattice<BitSet, ?> l = new Lattice();
BitSet b = new BitSet(n);
Node<BitSet> bot = new Node(b);
l.addNode(bot);
for (int i = 0; i < n; i++) {
BitSet bs = new BitSet(n);
bs.set(i, true);
Node<BitSet> next = new Node(bs);
l.addNode(next);
l.addEdge(bot, next);
recursiveBooleanAlgebra(next, l, n);
}
return l;
}
/**
* Returns the lattice of permutations of 1..n.
*
* Permutation are ordered as follows : A permutation s2 is a succesor of a
* permutation s1, if s2 is obtained from s1 by inverting two consecutive
* elements i and j such that before inversion j > i.
*
* Example : 124356 has following successors 214356, 142356, 124536 and
* 124365.
*
* The bottom of this lattice is identity (for exemple 123456) and the top
* is for instance 654321.
*
* @param n the lattice of permutations of the set 1..n
*
* @return the lattice of permutations of 1..n.
*/
public static Lattice permutationLattice(int n) {
Lattice<Permutation, ?> l = new Lattice();
int[] content = new int[n];
for (int i = 0; i < n; i++) {
content[i] = i;
}
Permutation s = new Permutation(n);
s.setContent(content);
Node<Permutation> bot = new Node(s);
l.addNode(bot);
for (int i = 0; i < n - 1; i++) {
int[] newC = content.clone();
newC[i] = content[i + 1];
newC[i + 1] = content[i];
Permutation newS = new Permutation(n);
newS.setContent(newC);
Node<Permutation> succ = new Node(newS);
l.addNode(succ);
l.addEdge(bot, succ);
recursivePermutationLattice(succ, l, n);
}
return l;
}
/**
* Returns the lattice cartesian product of l and r.
*
* A node in the product is a cartesian product of two nodes
*
* There is an edge (n1, m1) -> (n2, m2) if and only if there are edges n1
* -> n2 and m1 -> m2
*
* @param l Lattice of the left hand side of the product
* @param r Lattice of the right hand side of the product
*
* @return the lattice cartesian product of l and r
*/
public static Lattice product(Lattice l, Lattice r) {
Lattice prod = new Lattice();
// Create nodes
for (Object nL : l.getNodes()) {
for (Object nR : r.getNodes()) {
prod.addNode(new Node(new Couple(((Node) nL).getContent(), ((Node) nR).getContent())));
}
}
// Create edges
for (Object source : prod.getNodes()) {
for (Object target : prod.getNodes()) {
if (l.containsEdge(l.getNodeByContent(((Couple) ((Node) source).getContent()).getLeft()),
l.getNodeByContent(((Couple) ((Node) target).getContent()).getLeft()))
&& r.containsEdge(r.getNodeByContent(((Couple) ((Node) source).getContent()).getRight()),
r.getNodeByContent(((Couple) ((Node) target).getContent()).getRight()))) {
prod.addEdge((Node) source, (Node) target);
}
}
}
return prod;
}
/**
* Returns lattice l in which convex c has been doubled.
*
* @param l a lattice
* @param c a convex subset of l, to be doubled.
*
* @return a lattice construct from l by doubling the convex subset c.
*/
public static Lattice doublingConvex(Lattice l, DAGraph c) {
Lattice doubled = new Lattice();
// Copy nodes by Content
for (Object node : l.getNodes()) {
if (c.containsNode((Node) node)) {
// These nodes are doubled
Couple cpl0 = new Couple(((Node) node).getContent(), 0);
Node n0 = new Node(cpl0);
Couple cpl1 = new Couple(((Node) node).getContent(), 1);
Node n1 = new Node(cpl1);
doubled.addNode(n0);
doubled.addNode(n1);
} else {
// These nodes are just copied
doubled.addNode(new Node(((Node) node).getContent()));
}
}
// Construct edges of doubled
Couple test = new Couple(0, 0); // used to test class of contents
for (Object x : doubled.getNodes()) {
for (Object y : doubled.getNodes()) {
// Add an edge if x < y
if (((Node) x).getContent().getClass() == test.getClass()) { // x was in convex c
if (((Node) y).getContent().getClass() == test.getClass()) { // y was also in convex c
// x & y were in convex c
Couple cX = (Couple) ((Node) x).getContent();
Couple cY = (Couple) ((Node) y).getContent();
if ((cX.getLeft() == cY.getLeft()) && (((Integer) cX.getRight()) == 0)
&& (((Integer) cY.getRight()) == 1)) {
// Same content means same node. x is of the form (cX, 0) and y is of the for (cX, 1) so x < y in doubled.
doubled.addEdge((Node) x, (Node) y);
} else if (l.majorants(l.getNodeByContent(cX.getLeft())).contains(l.getNodeByContent(cY.getLeft()))
&& (cX.getRight() == cY.getRight())) {
// x < y in l and x & y have the same second component si x < y in doubled.
doubled.addEdge((Node) x, (Node) y);
}
} else { // y wasn't in convex c
// x was in c & y wasn't
Couple cX = (Couple) ((Node) x).getContent();
if (l.majorants(l.getNodeByContent(cX.getLeft())).contains(l.getNodeByContent(((Node) y).getContent()))
&& (((Integer) cX.getRight()) == 1)) {
// x < y in l and second component of x is 1.
doubled.addEdge((Node) x, (Node) y);
}
}
} else // x wasn't in convex c
if (((Node) y).getContent().getClass() == test.getClass()) { // y was in convex c
// x wasn't in c but y was
Couple cY = (Couple) ((Node) y).getContent();
if (l.majorants(l.getNodeByContent(((Node) x).getContent())).contains(l.getNodeByContent(cY.getLeft()))
&& (((Integer) cY.getRight()) == 0)) {
// x < y in l and x & second component of y is 0.
doubled.addEdge((Node) x, (Node) y);
}
} else // y wasn't in convex c
// x wasn't in c nor y
if (l.majorants(l.getNodeByContent(((Node) x).getContent())).contains(l.getNodeByContent(((Node) y).getContent()))) {
// x < y in l and x & second component of y is 0.
doubled.addEdge((Node) x, (Node) y);
}
}
}
doubled.transitiveReduction();
return doubled;
}
/**
* Computes successors of node n in the boolean algebra currently generated.
*
* @param node this method compute successors of this node
* @param l boolean algebra currently generated
* @param n the number of node of l will be 2^n at the end of computation
*/
private static void recursiveBooleanAlgebra(Node node, Lattice l, int n) {
for (int i = 0; i < n; i++) {
BitSet b = (BitSet) node.getContent();
if (!b.get(i)) {
BitSet bs = (BitSet) b.clone();
bs.set(i, true);
if (l.getNodeByContent(bs) == null) {
Node next = new Node(bs);
l.addNode(next);
l.addEdge(node, next);
recursiveBooleanAlgebra(next, l, n);
} else {
l.addEdge(node, l.getNodeByContent(bs));
}
}
}
}
/**
* Computes successors of node n in the lattice l.
*
* @param node successors of this node are computed by this method
* @param l lattice of permutations currently generated
* @param n lattice of permutation of the set 1..n is currently generated
*/
private static void recursivePermutationLattice(Node node, Lattice l, int n) {
Permutation s = (Permutation) node.getContent();
for (int i = 0; i < s.getLength() - 1; i++) {
if (s.getContent()[i] < s.getContent()[i + 1]) {
int[] newC = s.getContent().clone();
Node currentNode = new Node();
newC[i] = s.getContent()[i + 1];
newC[i + 1] = s.getContent()[i];
Permutation newP = new Permutation(n);
newP.setContent(newC);
boolean newNode = true;
Iterator<Node> it = l.getNodes().iterator();
while (it.hasNext() && newNode) {
currentNode = it.next();
Permutation currentContent = (Permutation) currentNode.getContent();
newNode = !(currentContent.equals(newP));
}
if (newNode) {
Permutation newS = new Permutation(n);
newS.setContent(newC);
Node next = new Node(newS);
l.addNode(next);
l.addEdge(node, next);
recursivePermutationLattice(next, l, n);
} else {
l.addEdge(node, currentNode);
}
}
}
}
/**
* Empty constructor.
*/
protected LatticeFactory() {
super();
}
/**
* This class provides a representation of permutations.
*
* If this component transforms : 0 -> 1, 1 -> 0 & 2 -> 2. Then its length
* is 3 and
*
* The content contains :
*
* ~~~
* content[0]=1 content[1]=0 content[2]=2
* ~~~
*/
private static class Permutation {
/**
* This component is a permutation of 0..length-1.
*/
private int length;
/**
* The transformation represented by this component.
*
* If this component transforms : 0 -> 1, 1 -> 0 & 2 -> 2. The field
* content contains :
*
* ~~~
* content[0]=1 content[1]=0 content[2]=2
* ~~~
*/
private int[] content;
/**
* Constructs identity of the set 0..n-1.
*
* @param n permutation of the set 0..n-1.
*/
Permutation(int n) {
this.length = n;
this.content = new int[n];
for (int i = 0; i < n; i++) {
this.content[i] = i;
}
}
/**
* Returns the transformation coded by this component.
*
* @return the transformation coded by this component.
*/
public int[] getContent() {
return this.content;
}
/**
* Set the transformation coded by this component.
*
* Length of this component is update by this method.
*
* @param c the transformation coded in this component.
*
* @return this for chaining
*/
public Permutation setContent(int[] c) {
this.content = c;
this.length = c.length;
return this;
}
/**
* Return length of this component.
*
* @return length of this component.
*/
public int getLength() {
return this.length;
}
/**
* Set length of this componenet.
*
* @param l length of this component.
*
* @return true if update is successful.
*/
public boolean setLength(int l) {
if ((this.content.length == l) && (l <= this.getLength())) {
this.length = l;
return true;
} else {
return false;
}
}
/**
* Returns a string representation of this component.
*
* @return a string representation of this component.
*/
@Override
public String toString() {
String str = "";
for (int i = 0; i < this.length; i++) {
str = str + this.content[i];
}
return str;
}
/**
* Returns true if this component is equal to s.
*
* @param s test if this component is equal to s
*
* @return true if this component is equal to s
*/
public boolean equals(Permutation s) {
if (!(s.getLength() == this.length)) {
return false;
} else {
boolean tmp = true;
for (int i = 0; i < this.length; i++) {
tmp = tmp && (this.content[i] == s.getContent()[i]);
}
return tmp;
}
}
/**
* Compute the hash code.
*
* @return an integer representing the object
*/
@Override
public int hashCode() {
return super.hashCode();
}
}
}