org.protempa.dest.table.Derivation Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of protempa-framework Show documentation
Show all versions of protempa-framework Show documentation
Protempa Framework is the core of Protempa.
/*
* #%L
* Protempa Framework
* %%
* Copyright (C) 2012 - 2013 Emory University
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
package org.protempa.dest.table;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.arp.javautil.arrays.Arrays;
import org.protempa.KnowledgeSource;
import org.protempa.KnowledgeSourceCache;
import org.protempa.KnowledgeSourceReadException;
import org.protempa.PropositionDefinition;
import org.protempa.proposition.Parameter;
import org.protempa.proposition.Proposition;
import org.protempa.proposition.TemporalProposition;
import org.protempa.proposition.UniqueId;
import org.protempa.proposition.interval.Relation;
import org.protempa.proposition.value.Value;
/**
* Traverses traversals from a proposition to/from a derived proposition.
*
* @author Andrew Post
*/
public final class Derivation extends Link {
private static final Value[] EMPTY_VALUE_ARRAY = new Value[0];
/**
* For configuring the direction of the traversal and how many derivation
* steps to traverse.
*/
public static enum Behavior {
/**
* Forward chaining, toward derived propositions.
*/
SINGLE_FORWARD,
/**
* Backward chaining, toward raw data.
*/
SINGLE_BACKWARD,
/**
* Forward chaining, toward derived propositions.
*/
MULT_FORWARD,
/**
* Backward chaining, toward raw data.
*/
MULT_BACKWARD
}
private final Behavior behavior;
private final Value[] allowedValues;
private Set knowledgeTree;
private final Relation relation;
private final Queue internalDerived;
public Derivation(String[] propositionIds, Behavior behavior) {
this(propositionIds, null, null, behavior, null);
}
public Derivation(String[] propositionIds,
PropertyConstraint[] constraints, Behavior behavior) {
this(propositionIds, constraints, null, -1, -1, null,
behavior, null);
}
public Derivation(String[] propositionIds,
PropertyConstraint[] constraints, Value[] allowedValues,
Behavior behavior) throws KnowledgeSourceReadException {
this(propositionIds, constraints, null, -1, -1, allowedValues,
behavior, null);
}
public Derivation(String[] propositionIds,
PropertyConstraint[] constraints, Value[] allowedValues,
Behavior behavior, Relation relation) {
this(propositionIds, constraints, null, -1, -1, allowedValues,
behavior, relation);
}
public Derivation(String[] propositionIds,
PropertyConstraint[] constraints,
Comparator comparator, int index,
Behavior behavior) throws KnowledgeSourceReadException {
this(propositionIds, constraints, comparator, index,
index >= 0 ? index + 1 : -1, null, behavior, null);
}
public Derivation(String[] propositionIds,
PropertyConstraint[] constraints,
Comparator comparator, int index,
Value[] allowedValues, Behavior behavior) {
this(propositionIds, constraints, comparator, index,
index >= 0 ? index + 1 : -1, allowedValues, behavior, null);
}
public Derivation(String[] propositionIds,
PropertyConstraint[] constraints,
Comparator comparator, int fromIndex, int toIndex,
Behavior behavior) throws KnowledgeSourceReadException {
this(propositionIds, constraints, comparator, fromIndex, toIndex,
null, behavior, null);
}
public Derivation(String[] propositionIds,
PropertyConstraint[] constraints,
Comparator comparator, int fromIndex, int toIndex,
Value[] allowedValues, Behavior behavior, Relation relation) {
super(propositionIds, constraints, comparator, fromIndex, toIndex);
if (allowedValues == null) {
this.allowedValues = EMPTY_VALUE_ARRAY;
} else {
this.allowedValues = allowedValues.clone();
}
if (behavior == null) {
throw new IllegalArgumentException("behavior cannot be null");
}
this.behavior = behavior;
this.relation = relation;
this.internalDerived = new LinkedList<>();
}
@Override
public String[] getInferredPropositionIds(KnowledgeSource knowledgeSource,
String[] inPropIds) throws KnowledgeSourceReadException {
String[] explicitPropIds = getPropositionIds();
if (explicitPropIds.length > 0) {
return explicitPropIds;
} else {
Set result = new HashSet<>();
for (PropositionDefinition propDef : knowledgeSource.readPropositionDefinitions(inPropIds)) {
switch (this.behavior) {
case SINGLE_BACKWARD:
Arrays.addAll(result, propDef.getChildren());
break;
case MULT_BACKWARD:
Queue backwardProps
= new LinkedList<>();
Arrays.addAll(backwardProps, propDef.getChildren());
String pId;
while ((pId = backwardProps.poll()) != null) {
PropositionDefinition pDef
= knowledgeSource.readPropositionDefinition(pId);
Arrays.addAll(backwardProps, pDef.getChildren());
}
result.addAll(backwardProps);
break;
case SINGLE_FORWARD:
for (PropositionDefinition def
: knowledgeSource.readParents(propDef)) {
result.add(def.getId());
}
break;
case MULT_FORWARD:
Queue forwardProps
= new LinkedList<>();
for (PropositionDefinition def
: knowledgeSource.readParents(propDef)) {
forwardProps.add(def.getId());
}
// pId is declared in MULT_BACKWARD case.
while ((pId = forwardProps.poll()) != null) {
PropositionDefinition pDef
= knowledgeSource.readPropositionDefinition(pId);
for (PropositionDefinition def
: knowledgeSource.readParents(pDef)) {
forwardProps.add(def.getId());
}
}
result.addAll(forwardProps);
break;
default:
throw new AssertionError(
"Invalid derivation behavior specified");
}
}
return result.toArray(new String[result.size()]);
}
}
@Override
String headerFragment() {
return createHeaderFragment("derived");
}
/**
* Traverses a derivation.
*
* @param proposition a {@link Proposition} at which to start the traversal.
* @param forwardDerivations a {@link Map>} of
* derived propositions.
* @param backwardDerivations a {@link Map>}
* of derived propositions.
* @param references a {@link Map} of unique
* identifiers to {@link Proposition}s, used to resolve references.
* @param knowledgeSource the {@link KnowledgeSource}.
* @param cache a {@link Set} for convenience in checking if
* duplicate propositions are traversed to. It is cleared in between calls
* to this method.
* @return the {@link Collection} at the end of the traversal
* step.
*/
@Override
Collection traverse(Proposition proposition,
Map> forwardDerivations,
Map> backwardDerivations,
Map references,
KnowledgeSourceCache ksCache, final Set cache) {
//First, aggregate derived values according to the specified behavior.
List derived = null;
switch (this.behavior) {
case SINGLE_FORWARD:
derived = filterMatches(proposition,
forwardDerivations.get(proposition),
cache);
break;
case SINGLE_BACKWARD:
derived = filterMatches(proposition,
backwardDerivations.get(proposition),
cache);
break;
case MULT_FORWARD:
populateKnowledgeTree(ksCache);
if (this.knowledgeTree.contains(proposition.getId())) {
/*
* For performance, pull the specified proposition ids,
* cache the hierarchy, and check to see if each
* traversed-to proposition's id is in the hierarchy. This
* is much faster than traversing the whole hierarchy in
* most cases.
*/
derived = new ArrayList<>();
internalDerived.add(proposition);
while (!internalDerived.isEmpty()) {
Proposition prop = internalDerived.remove();
/*
* If (in hierarchy or ???) and not in cache, then add
* to derived.
*/
Collection c
= forwardDerivations.get(prop);
if (c != null) {
for (Proposition p : c) {
if (cache.add(p)) {
internalDerived.add(p);
if (isMatch(p) && hasAllowedValue(p)) {
derived.add(p);
}
}
}
}
}
}
break;
case MULT_BACKWARD:
derived = new ArrayList<>();
internalDerived.add(proposition);
while (!internalDerived.isEmpty()) {
Proposition prop = internalDerived.remove();
Collection c
= backwardDerivations.get(prop);
if (c != null) {
for (Proposition p : c) {
if (cache.add(p)) {
internalDerived.add(p);
if (isMatch(p) && hasAllowedValue(p)) {
derived.add(p);
}
}
}
}
}
break;
default:
throw new AssertionError("Unexpected behavior: "
+ this.behavior);
}
this.internalDerived.clear();
return createResults(derived);
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
private void populateKnowledgeTree(KnowledgeSourceCache ksCache) {
if (this.knowledgeTree == null) {
this.knowledgeTree = new HashSet<>();
Queue queue = new LinkedList<>();
Arrays.addAll(queue, getPropositionIds());
String propId;
while ((propId = queue.poll()) != null) {
PropositionDefinition propDef = ksCache.get(propId);
if (propDef != null) {
if (propDef.getInDataSource()) {
this.knowledgeTree.add(propDef.getId());
}
Arrays.addAll(queue, propDef.getChildren());
} else {
throw new AssertionError("Invalid proposition definition " + propId);
}
}
}
}
private boolean hasAllowedValue(Proposition proposition) {
if (this.allowedValues.length == 0) {
return true;
} else {
if (proposition instanceof Parameter
&& Arrays.contains(this.allowedValues,
((Parameter) proposition).getValue())) {
return true;
} else {
return false;
}
}
}
/**
* Filters propositions
. Removes duplicates, propositions not
* in the proposition id list, and parameters that do not meet any value
* constraints set.
*
* @param propositions the {@link Collection} to filter.
* @param cache a {@link Set} that is used to find duplicates.
*
* @return a newly-created {@link List}.
*/
private List filterMatches(Proposition inProposition,
Collection propositions, Set cache) {
TemporalProposition inTp = null;
if (this.relation != null) {
if (inProposition instanceof TemporalProposition) {
inTp = (TemporalProposition) inProposition;
}
}
List result = new ArrayList<>();
if (propositions != null) {
for (Proposition proposition : propositions) {
if (cache.add(proposition)
&& isMatch(proposition)
&& hasAllowedValue(proposition)) {
if (inTp != null && proposition instanceof TemporalProposition) {
TemporalProposition tp = (TemporalProposition) proposition;
if (!this.relation.hasRelation(tp.getInterval(), inTp.getInterval())) {
continue;
}
}
result.add(proposition);
}
}
}
return result;
}
public Behavior getBehavior() {
return behavior;
}
public Value[] getAllowedValues() {
return allowedValues;
}
public Relation getRelation() {
return relation;
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + java.util.Arrays.hashCode(allowedValues);
result = prime * result + ((behavior == null) ? 0 : behavior.hashCode());
result = prime * result + ((knowledgeTree == null) ? 0 : knowledgeTree.hashCode());
result = prime * result + ((relation == null) ? 0 : relation.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!super.equals(obj)) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Derivation other = (Derivation) obj;
if (!java.util.Arrays.equals(allowedValues, other.allowedValues)) {
return false;
}
if (behavior != other.behavior) {
return false;
}
if (knowledgeTree == null) {
if (other.knowledgeTree != null) {
return false;
}
} else if (!knowledgeTree.equals(other.knowledgeTree)) {
return false;
}
if (relation == null) {
if (other.relation != null) {
return false;
}
} else if (!relation.equals(other.relation)) {
return false;
}
return true;
}
}