/*******************************************************************************
* Copyright 2011
* Ubiquitous Knowledge Processing (UKP) Lab
* Technische Universität Darmstadt
*
* 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.
******************************************************************************/
package org.dkpro.lab.task;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import org.dkpro.lab.task.impl.DimensionBundle;
import org.dkpro.lab.task.impl.DynamicDimension;
public class ParameterSpace implements Iterable<Map<String, Object>>
{
private Dimension<?>[] dimensions;
private Set<Constraint> constraints;
private int stepCount = 0;
public ParameterSpace()
{
dimensions = new Dimension[0];
constraints = new HashSet<Constraint>();
}
public ParameterSpace(Dimension<?>... aDimensions)
{
dimensions = aDimensions;
constraints = new HashSet<Constraint>();
}
public void reset()
{
stepCount = 0;
for (Dimension<?> d : dimensions) {
d.rewind();
}
}
public void setDimensions(Dimension<?>[] aDimensions)
{
if (aDimensions == null) {
dimensions = new Dimension[0];
}
else {
dimensions = new Dimension[aDimensions.length];
System.arraycopy(aDimensions, 0, dimensions, 0, dimensions.length);
}
reset();
}
public Dimension<?>[] getDimensions()
{
return dimensions;
}
/**
* Gets the number of steps taken in the parameter space. Steps that are skipped due to a
* {@link Constraint} are counted as well.
*/
public int getStepCount()
{
return stepCount;
}
/**
* Add the constraint.
*
* @see #getConstraints()
*/
public void addConstraint(Constraint aConstraint)
{
constraints.add(aConstraint);
}
/**
* Remove the constraint.
*
* @see #getConstraints()
*/
public void removeConstraint(Constraint aConstraint)
{
constraints.remove(aConstraint);
}
public void setConstraints(Set<Constraint> aConstraints)
{
constraints = new HashSet<Constraint>();
if (aConstraints != null) {
constraints.addAll(aConstraints);
}
}
/**
* Get the constraints. If no constraints are set, all parameter combinations are executed. If
* constraints are set, a parameter combination is only executed if at least one constraint
* allows the combination, otherwise the combination is skipped.
*/
public Set<Constraint> getConstraints()
{
return constraints;
}
@Override
public Iterator<Map<String, Object>> iterator()
{
if (dimensions.length > 0) {
return new ParameterSpaceIterator();
}
else {
// Run once with empty configuration map if no dimensions are given
Set<Map<String, Object>> dummy = new HashSet<Map<String, Object>>();
dummy.add(new HashMap<String, Object>());
return dummy.iterator();
}
}
private class ParameterSpaceIterator
implements Iterator<Map<String, Object>>
{
private int incDim = -3;
public ParameterSpaceIterator()
{
step();
}
private void step()
{
do {
// Initialize
if (incDim == -3) {
for (Dimension<?> d : dimensions) {
d.rewind();
try {
d.next();
}
catch (NoSuchElementException e) {
// No need to move the cursor to the first element in empty dimensions.
}
}
incDim = dimensions.length - 1;
if (constraintsMet()) {
return;
}
}
if (incDim < 0) {
return; // Nothing more to iterate over
}
if (dimensions[incDim].hasNext()) {
dimensions[incDim].next();
}
else {
while (!dimensions[incDim].hasNext()) {
dimensions[incDim].rewind();
try {
dimensions[incDim].next();
}
catch (NoSuchElementException e) {
// No need to move the cursor to the first element in empty dimensions.
}
incDim--;
if (incDim < 0) {
return; // Nothing more to iterate over
}
}
dimensions[incDim].next();
incDim = dimensions.length - 1;
}
}
while (!constraintsMet());
}
private boolean constraintsMet()
{
Map<String, Object> config = current();
stepCount++;
// If no constraints are set, run everything
if (constraints.isEmpty()) {
return true;
}
// If constraints are set, run a configuration if any of them says "ok"
for (Constraint c : constraints) {
if (c.isValid(config)) {
return true;
}
}
// default to not running
return false;
}
@Override
public boolean hasNext()
{
return incDim > -1;
}
public Map<String, Object> current()
{
Map<String, Object> config = new LinkedHashMap<String, Object>();
// Pass 1: no dynamic dimensions
for (Dimension<?> d : dimensions) {
try {
if (d instanceof DimensionBundle<?>) {
DimensionBundle<?> bundle = ((DimensionBundle<?>) d);
String bundleId = bundle.getBundleId();
if (bundle.getName() != null && bundleId != null) {
config.put(bundle.getName(), bundle.getBundleId());
}
config.putAll(bundle.current());
}
else if (d instanceof DynamicDimension) {
// defer
}
else {
config.put(d.getName(), d.current());
}
}
catch (NoSuchElementException e) {
// Empty dimensions contribute nothing
}
}
// Pass 2: dynamic dimensions
for (Dimension<?> d : dimensions) {
if (d instanceof DynamicDimension) {
try {
((DynamicDimension) d).setConfiguration(config);
config.put(d.getName(), d.current());
}
catch (NoSuchElementException e) {
// Empty dimensions contribute nothing
}
}
}
return config;
}
@Override
public Map<String, Object> next()
{
try {
return current();
}
finally {
step();
}
}
@Override
public void remove()
{
throw new UnsupportedOperationException("No no");
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("[");
for (Dimension<?> d : dimensions) {
sb.append(d);
}
sb.append("]");
return sb.toString();
}
}
}