/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* LiteralSet.java
* Copyright (C) 2003 Peter A. Flach, Nicolas Lachiche
*
* Thanks to Amelie Deltour for porting the original C code to Java
* and integrating it into Weka.
*/
package weka.associations.tertius;
import weka.core.Instance;
import weka.core.Instances;
import weka.core.RevisionHandler;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
/**
* Class representing a set of literals, being either the body or the head
* of a rule.
*
* @author <a href="mailto:adeltour@netcourrier.com">Amelie Deltour</a>
* @version $Revision: 1.7 $
*/
public abstract class LiteralSet
implements Serializable, Cloneable, RevisionHandler {
/** for serialization */
private static final long serialVersionUID = 6094536488654503152L;
/** Literals contained in this set. */
private ArrayList m_literals;
/** Last literal added to this set. */
private Literal m_lastLiteral;
/** Number of instances in the data the set deals with. */
private int m_numInstances;
/** Set of counter-instances of this part of the rule. */
private ArrayList m_counterInstances;
/* For a body, counter-instances are the instances satisfying the body.
* For a head, conter-instances are the instances satisfying the negation. */
/** Counter for the number of counter-instances. */
private int m_counter;
/**
* Type of properties expressed in this set
* (individual or parts properties).
*/
private int m_type;
/**
* Constructor for a set that does not store its counter-instances.
*/
public LiteralSet() {
m_literals = new ArrayList();
m_lastLiteral = null;
m_counterInstances = null;
m_type = -1;
}
/**
* Constructor initializing the set of counter-instances to all the instances.
*
* @param instances The dataset.
*/
public LiteralSet(Instances instances) {
this();
m_numInstances = instances.numInstances();
m_counterInstances = new ArrayList(m_numInstances);
Enumeration enu = instances.enumerateInstances();
while (enu.hasMoreElements()) {
m_counterInstances.add(enu.nextElement());
}
}
/**
* Returns a shallow copy of this set.
* The structured is copied but the literals themselves are not copied.
*
* @return A copy of this LiteralSet.
*/
public Object clone() {
Object result = null;
try {
result = super.clone();
/* Clone the set of literals, but not the literals themselves. */
((LiteralSet) result).m_literals = (ArrayList) m_literals.clone();
if (m_counterInstances != null) {
/* Clone the set of instances, but not the instances themselves. */
((LiteralSet) result).m_counterInstances
= (ArrayList) m_counterInstances.clone();
}
} catch(Exception e) {
/* An exception is not supposed to happen here. */
e.printStackTrace();
System.exit(0);
}
return result;
}
/**
* Update the number of counter-instances of this set in the dataset.
* This method should be used is the set does not store its counter-instances.
*
* @param instances The dataset.
*/
public void upDate(Instances instances) {
Enumeration enu = instances.enumerateInstances();
m_numInstances = instances.numInstances();
m_counter = 0;
while (enu.hasMoreElements()) {
if (this.counterInstance((Instance) enu.nextElement())) {
m_counter++;
}
}
}
/**
* Get the number of counter-instances of this LiteralSet.
*
* @return The number of counter-instances.
*/
public int getCounterInstancesNumber() {
if (m_counterInstances != null) {
return m_counterInstances.size();
} else {
return m_counter;
}
}
/**
* Get the frequency of counter-instances of this LiteralSet in the data.
*
* @return The frequency of counter-instances.
*/
public double getCounterInstancesFrequency() {
return (double) getCounterInstancesNumber() / (double) m_numInstances;
}
/**
* Test if this LiteralSet has more counter-instances than the threshold.
*
* @param minFrequency The frequency threshold.
* @return True if there are more counter-instances than the threshold.
*/
public boolean overFrequencyThreshold(double minFrequency) {
return getCounterInstancesFrequency() >= minFrequency;
}
/**
* Test if all the intances are counter-instances.
*
* @return True if all the instances are counter-instances.
*/
public boolean hasMaxCounterInstances() {
return getCounterInstancesNumber() == m_numInstances;
}
/**
* Add a Literal to this set.
*
* @param element The element to add.
*/
public void addElement(Literal element) {
m_literals.add(element);
/* Update the last literal. */
m_lastLiteral = element;
/* Update the type in the case of individual-based learning. */
if (element instanceof IndividualLiteral) {
int type = ((IndividualLiteral) element).getType();
if (type > m_type) {
m_type = type;
}
}
/* Update the set of counter-instances. */
if (m_counterInstances != null) {
for (int i = m_counterInstances.size() - 1; i >= 0; i--) {
Instance current = (Instance) m_counterInstances.get(i);
if (!canKeep(current, element)) {
m_counterInstances.remove(i);
}
}
}
}
/**
* Test if this set is empty.
*
* @return True if the set is empty.
*/
public final boolean isEmpty() {
return m_literals.size() == 0;
}
/**
* Give the number of literals in this set.
*
* @return The number of literals.
*/
public final int numLiterals() {
return m_literals.size();
}
/**
* Enumerate the literals contained in this set.
*
* @return An Iterator for the literals.
*/
public final Iterator enumerateLiterals() {
return m_literals.iterator();
}
/**
* Give the last literal added to this set.
*
* @return The last literal added.
*/
public Literal getLastLiteral() {
return m_lastLiteral;
}
/**
* Test if the negation of this LiteralSet is included in another LiteralSet.
*
* @param otherSet The other LiteralSet.
* @return True if the negation of this LiteralSet is included in
* the other LiteralSet.
*/
public boolean negationIncludedIn(LiteralSet otherSet) {
Iterator iter = this.enumerateLiterals();
while (iter.hasNext()) {
Literal current = (Literal) iter.next();
if (!otherSet.contains(current.getNegation())) {
return false;
}
}
return true;
}
/**
* Test if this LiteralSet contains a given Literal.
*
* @param lit The literal that is looked for.
* @return True if this literal is contained in this LiteralSet.
*/
public boolean contains(Literal lit) {
return m_literals.contains(lit);
}
/**
* Give the type of properties in this set (individual or part properties).
*/
public int getType() {
return m_type;
}
/**
* Test if an individual instance, given a part instance of this individual,
* is a counter-instance of this LiteralSet.
*
* @param individual The individual instance.
* @param part The part instance.
* @return True if the individual is a counter-instance.
*/
public boolean counterInstance(Instance individual, Instance part) {
Iterator iter = this.enumerateLiterals();
while (iter.hasNext()) {
IndividualLiteral current = (IndividualLiteral) iter.next();
if (current.getType() == IndividualLiteral.INDIVIDUAL_PROPERTY
&& !canKeep(individual, current)) {
return false;
} else if (current.getType() == IndividualLiteral.PART_PROPERTY
&& !canKeep(part, current)) {
return false;
}
}
return true;
}
/**
* Test if an instance is a counter-instance of this LiteralSet.
*
* @param instance The instance to test.
* @return True if the instance is a counter-instance.
*/
public boolean counterInstance(Instance instance) {
if ((instance instanceof IndividualInstance)
&& (m_type == IndividualLiteral.PART_PROPERTY)) {
/* In the case of an individual instance, all the parts of the individual
* have to be considered.
* Part properties can be found in the body only, so here we test for
* an instance satisfying the set.
* It satisfies the set if there exists a part satisfying the set.
*/
Enumeration enu
= ((IndividualInstance) instance).getParts().enumerateInstances();
while (enu.hasMoreElements()) {
if (counterInstance(instance, (Instance) enu.nextElement())) {
return true;
}
}
return false;
} else {
/* Usual case. */
Iterator iter = this.enumerateLiterals();
while (iter.hasNext()) {
Literal current = (Literal) iter.next();
if (!canKeep(instance, current)) {
return false;
}
}
return true;
}
}
/**
* Test if an instance can be kept as a counter-instance,
* given a new literal.
*
* @param instance The instance to test.
* @param newLit The new literal.
* @return True if the instance is still a counter-instance.
*/
public abstract boolean canKeep(Instance instance, Literal newLit);
/**
* Test if this LiteralSet is included in a rule.
*
* @param otherRule The rule to test.
* @return True if this set of literals is included in the rule.
*/
public abstract boolean isIncludedIn(Rule otherRule);
/**
* Gives a String representation for this set of literals.
*/
public abstract String toString();
}