diff --git a/src/core/de.evoal.core/src/main/java/de/evoal/core/api/ea/constraints/strategies/fitness/MalusForFitnessStrategy.java b/src/core/de.evoal.core/src/main/java/de/evoal/core/api/ea/constraints/strategies/fitness/MalusForFitnessStrategy.java index 8da60569df1af67c5502bb65bf7449563a044392..c6d19237bdb0e9b48de7c196e459d4814d5a96de 100644 --- a/src/core/de.evoal.core/src/main/java/de/evoal/core/api/ea/constraints/strategies/fitness/MalusForFitnessStrategy.java +++ b/src/core/de.evoal.core/src/main/java/de/evoal/core/api/ea/constraints/strategies/fitness/MalusForFitnessStrategy.java @@ -39,9 +39,9 @@ public class MalusForFitnessStrategy implements HandlingStrategy { * @param candidate The individual. * @param fitnessValues The calculated fitness values. */ - public void apply(final Properties candidate, double[] fitnessValues) { - for(int index = 0; index < fitnessValues.length; ++index) { - fitnessValues[index] = malusConversions[index].apply(candidate, fitnessValues[index]); + public void apply(final Properties genotype, final Properties fitness) { + for(int index = 0; index < fitness.size(); ++index) { + fitness.set(index, malusConversions[index].apply(genotype, fitness, fitness.getAsDouble(index))); } } } diff --git a/src/core/de.evoal.core/src/main/java/de/evoal/core/api/ea/constraints/strategies/fitness/MalusFunction.java b/src/core/de.evoal.core/src/main/java/de/evoal/core/api/ea/constraints/strategies/fitness/MalusFunction.java index d1d78bd6d8ecfad32450ef6cd2bbaec320a88a6b..f05f19b9b929957117a9e4570cb8bd08a4801771 100644 --- a/src/core/de.evoal.core/src/main/java/de/evoal/core/api/ea/constraints/strategies/fitness/MalusFunction.java +++ b/src/core/de.evoal.core/src/main/java/de/evoal/core/api/ea/constraints/strategies/fitness/MalusFunction.java @@ -14,5 +14,5 @@ public interface MalusFunction { * @param fitnessValue The current fitness value. * @return The adapted fitness value. */ - public double apply(final Properties properties, final double fitnessValue); + public double apply(final Properties genoProperties, final Properties fitnessPro, final double fitnessValue); } diff --git a/src/core/de.evoal.core/src/main/java/de/evoal/core/api/ea/constraints/strategies/fitness/internal/ChainFunction.java b/src/core/de.evoal.core/src/main/java/de/evoal/core/api/ea/constraints/strategies/fitness/internal/ChainFunction.java index f16ef1f672f98499c62d30770dc3c329cfec4f29..1e78559538c904a0747faf1727a056cd00e10efd 100644 --- a/src/core/de.evoal.core/src/main/java/de/evoal/core/api/ea/constraints/strategies/fitness/internal/ChainFunction.java +++ b/src/core/de.evoal.core/src/main/java/de/evoal/core/api/ea/constraints/strategies/fitness/internal/ChainFunction.java @@ -13,9 +13,9 @@ public class ChainFunction implements MalusFunction { } @Override - public double apply(final Properties properties, final double fitnessValue) { - double adaptedFitnessValue = child.apply(properties, fitnessValue); + public double apply(final Properties genoProperties, final Properties fitnessProperties, final double fitnessValue) { + double adaptedFitnessValue = child.apply(genoProperties, fitnessProperties, fitnessValue); - return strategy.apply(properties, adaptedFitnessValue); + return strategy.apply(genoProperties, fitnessProperties, adaptedFitnessValue); } } diff --git a/src/core/de.evoal.core/src/main/java/de/evoal/core/api/ea/constraints/strategies/fitness/internal/IdentityFunction.java b/src/core/de.evoal.core/src/main/java/de/evoal/core/api/ea/constraints/strategies/fitness/internal/IdentityFunction.java index 8ca05256c3ef8af8855ce4c3beaabb38dad91b0c..268b71c7354233a3a684cec01c6aed298dee57bb 100644 --- a/src/core/de.evoal.core/src/main/java/de/evoal/core/api/ea/constraints/strategies/fitness/internal/IdentityFunction.java +++ b/src/core/de.evoal.core/src/main/java/de/evoal/core/api/ea/constraints/strategies/fitness/internal/IdentityFunction.java @@ -5,7 +5,7 @@ import de.evoal.core.api.properties.Properties; public class IdentityFunction implements MalusFunction { @Override - public double apply(final Properties properties, final double fitnessValue) { + public double apply(final Properties genoProperties, final Properties fitnessProperties, final double fitnessValue) { return fitnessValue; } } diff --git a/src/core/de.evoal.core/src/main/java/de/evoal/core/api/utils/LanguageHelper.java b/src/core/de.evoal.core/src/main/java/de/evoal/core/api/utils/LanguageHelper.java index cbb3117e80f2c8a3d6c45923a0e0bb4cf506352d..2e3d9a37b4c89c8d3381f93d4bf7827823c6592e 100644 --- a/src/core/de.evoal.core/src/main/java/de/evoal/core/api/utils/LanguageHelper.java +++ b/src/core/de.evoal.core/src/main/java/de/evoal/core/api/utils/LanguageHelper.java @@ -82,4 +82,8 @@ public final class LanguageHelper { public static Predicate<? super Value> filterInstanceByType(final String instanceTypeName) { return i -> instanceTypeName.equals(((Instance)i).getName().getName()); } + + public static Predicate<? super Instance> filterByAttributesInstanceType(final String attributeName, final String attributeTypeName) { + return i -> attributeTypeName.equals(((Instance) i.findAttribute(attributeName).getValue()).getName().getName()); + } } diff --git a/src/core/de.evoal.core/src/main/java/de/evoal/core/main/ea/constraints/constraint/strategies/constraint/JeneticsConstraintProducer.java b/src/core/de.evoal.core/src/main/java/de/evoal/core/main/ea/constraints/constraint/strategies/constraint/JeneticsConstraintProducer.java index e57067ec77fb2ef9ef2f48f1e0731337a6eac66a..28ab5ec485e2bf8355ad0ca83e1f63e3039617cf 100644 --- a/src/core/de.evoal.core/src/main/java/de/evoal/core/main/ea/constraints/constraint/strategies/constraint/JeneticsConstraintProducer.java +++ b/src/core/de.evoal.core/src/main/java/de/evoal/core/main/ea/constraints/constraint/strategies/constraint/JeneticsConstraintProducer.java @@ -15,6 +15,7 @@ import org.apache.deltaspike.core.api.provider.BeanProvider; import javax.enterprise.context.ApplicationScoped; import javax.enterprise.inject.Produces; +import javax.inject.Named; import java.util.*; import java.util.stream.Collectors; @@ -24,55 +25,22 @@ public class JeneticsConstraintProducer { @Produces public List<io.jenetics.engine.Constraint> create( final @ConfigurationValue(entry = BlackboardEntry.EA_CONFIGURATION, access = "algorithm.handlers") Array handlerConfigurations, - final @ConfigurationValue(entry = BlackboardEntry.EA_CONFIGURATION, access = "algorithm.fitness") Instance fitnessConfiguration, - final Constraints constraints, + final @Named("optimization-function-output") PropertiesSpecification optimizationSpec, + final @Named("optimization-function") FitnessFunction function, final CustomCodec codec, + final Constraints constraints, final CalculationFactory factory) { + // collect group information to handle final List<Instance> groups = ConfigurationUtils.findConstraintHandlerByHandlingStrategy(handlerConfigurations, "kill-at-birth"); final Set<String> allGroups = groups.stream().map(i -> (String)((LiteralValue)i.findAttribute("category").getValue()).getLiteral().getValue()).collect(Collectors.toSet()); final List<Constraint> listOfConstraints = constraints.getConstraints(); - final FitnessFunction function = create(fitnessConfiguration); - final PropertiesSpecification fitnessSpec = toSpecification(fitnessConfiguration); - - final List<io.jenetics.engine.Constraint> result = listOfConstraints + return listOfConstraints .stream() .filter(c -> allGroups.contains(c.getGroup())) .map(factory::create) - .map(s -> new JeneticsConstraintStrategy(s, codec, function, fitnessSpec, new RandomGenotypeStrategy())) + .map(s -> new JeneticsConstraintStrategy(s, codec, function, optimizationSpec, new RandomGenotypeStrategy())) .collect(Collectors.toList()); - - return result; - } - - private PropertiesSpecification toSpecification(final Instance fitnessConfiguration) { - return PropertiesSpecification.builder() - .add(((Array)findInner(fitnessConfiguration).findAttribute("maps-to").getValue()) - .getValues() - .stream() - .map(DataReference.class::cast) - .map(DataReference::getDefinition) - ) - .build(); - } - - private FitnessFunction create(Instance fitnessConfig) { - fitnessConfig = findInner(fitnessConfig); - - final String fitnessName = fitnessConfig.getName().getName(); - - return BeanProvider.getContextualReference(fitnessName, false, FitnessFunction.class) - .init(fitnessConfig); - } - - private Instance findInner(final Instance fitnessConfig) { - final Attribute subFunction = fitnessConfig.findAttribute("function"); - - if(subFunction != null) { - return findInner((Instance)subFunction.getValue()); - } - - return fitnessConfig; } } diff --git a/src/core/de.evoal.core/src/main/java/de/evoal/core/main/ea/constraints/constraint/strategies/fitness/MalusFunctionProducer.java b/src/core/de.evoal.core/src/main/java/de/evoal/core/main/ea/constraints/constraint/strategies/fitness/MalusFunctionProducer.java index 449b23a7ff20c72a42d7305efcb990765f6e55a4..7be9fa403aa9d0af8e0349061d080db25a2d04c3 100644 --- a/src/core/de.evoal.core/src/main/java/de/evoal/core/main/ea/constraints/constraint/strategies/fitness/MalusFunctionProducer.java +++ b/src/core/de.evoal.core/src/main/java/de/evoal/core/main/ea/constraints/constraint/strategies/fitness/MalusFunctionProducer.java @@ -17,57 +17,74 @@ import javax.enterprise.inject.Produces; import de.evoal.core.main.ea.constraints.constraint.strategies.fitness.internal.MalusForFitnessFunction; import de.evoal.core.main.ea.constraints.constraint.utils.ConfigurationUtils; -import de.evoal.languages.model.instance.Array; -import de.evoal.languages.model.instance.Attribute; -import de.evoal.languages.model.instance.Instance; -import de.evoal.languages.model.instance.Misc; +import de.evoal.languages.model.instance.*; import org.apache.commons.math3.util.Pair; import javax.inject.Named; import java.util.*; +import java.util.stream.Collectors; @ApplicationScoped public class MalusFunctionProducer { @ApplicationScoped @Produces public MalusForFitnessStrategy create( final @ConfigurationValue(entry = BlackboardEntry.EA_CONFIGURATION, access = "algorithm.handlers") Array handlers, - final Constraints constraints, - final @Named("genotype-specification") PropertiesSpecification source, + final @Named("optimization-function-input") PropertiesSpecification source, + final @Named("optimization-function-output") PropertiesSpecification target, final @Named("output-dependencies") PropertiesDependencies dependencies, - final @Named("surrogate-target-properties-specification") PropertiesSpecification target, // FIXME I don't think that this is correct here since we may not have a surrogate function at hand. But we have to define this usecase in order to be able to fix this issue. + final Constraints constraints, final CalculationFactory factory) { - final MalusForFitnessStrategy resultingFunction = new MalusForFitnessStrategy(target.size()); + // select constraint handlers that use malus-for-fitness + final List<Instance> relevantHandlers = ConfigurationUtils.findConstraintHandlerByHandlingStrategy(handlers, "malus-for-fitness"); - // collect group information to handle - final List<Instance> groups = ConfigurationUtils.findConstraintHandlerByHandlingStrategy(handlers, "malus-for-fitness"); + // collect group names of relevant handlers + final Set<String> allGroups = relevantHandlers.stream().map(i -> (String)((LiteralValue)i.findAttribute("category").getValue()).getLiteral().getValue()).collect(Collectors.toSet()); + + // resulting strategies + final MalusForFitnessStrategy resultingFunction = new MalusForFitnessStrategy(target.size()); // collect all constraints for each index (of the appropriate groups) - final List<List<Pair<Constraint, Instance>>> constraintsForIndex = new ArrayList<>(source.size()); + final List<List<Pair<Constraint, Instance>>> constraintsBySourceIndex = new ArrayList<>(source.size()); for(int index = 0; index < source.size(); ++index) { - constraintsForIndex.add(new ArrayList<>()); + constraintsBySourceIndex.add(new ArrayList<>()); } - for(final Constraint constraint : constraints.getConstraints()) { - final String group = constraint.getGroup(); + final List<List<Pair<Constraint, Instance>>> constraintsByTargetIndex = new ArrayList<>(target.size()); + for(int index = 0; index < target.size(); ++index) { + constraintsByTargetIndex.add(new ArrayList<>()); + } - for(final Instance info : groups) { - if(((Misc)info.getName()).getName().equals(group)) { + constraints.getConstraints() + .stream() + .filter(c -> allGroups.contains(c.getGroup())) + .forEach(constraint -> { for(final PropertySpecification ps : constraint.getUsedProperties()) { - final int index = source.indexOf(ps); - constraintsForIndex.get(index).add(new Pair<>(constraint, info)); + if(source.equals(ps)) { + // handle source specification + final int index = source.indexOf(ps); + final Instance configuration = ConfigurationUtils.findConstraintHandlerByHandlingStrategyAndCategory(handlers, "malus-for-fitness", constraint.getGroup()); + constraintsBySourceIndex.get(index) + .add(new Pair<>(constraint, configuration)); + } else { + // handle target specification + final int index = target.indexOf(ps); + final Instance configuration = ConfigurationUtils.findConstraintHandlerByHandlingStrategyAndCategory(handlers, "malus-for-fitness", constraint.getGroup()); + constraintsByTargetIndex.get(index) + .add(new Pair<>(constraint, configuration)); + } } - } - } - } + }); - // build MalusFunctionParts + // build actual malus function for each output for(int index = 0; index < target.size(); ++index) { + // keep track of already applied constraints final Set<Constraint> applied = new HashSet<>(); + // iterate over input property specifications that influence the current output specification for(final PropertySpecification ips : dependencies.get(target.getProperties().get(index))) { final int ipsIndex = source.indexOf(ips); - for(final Pair<Constraint, Instance> pair : constraintsForIndex.get(ipsIndex)) { + for(final Pair<Constraint, Instance> pair : constraintsBySourceIndex.get(ipsIndex)) { final Constraint constraint = pair.getFirst(); final Instance configuration = pair.getSecond(); @@ -85,6 +102,25 @@ public class MalusFunctionProducer { resultingFunction.add(index, strategy); } } + + // iterate over target constraints + for(final Pair<Constraint, Instance> pair : constraintsByTargetIndex.get(index)) { + final Constraint constraint = pair.getFirst(); + final Instance configuration = pair.getSecond(); + + // prevent constraints from being applied multiple times per fitness value + if(applied.contains(constraint)) { + continue; + } + applied.add(constraint); + + final CalculationStrategy calculation = factory.create(constraint); + + final String handlerName = LanguageHelper.lookup(configuration, "name"); + + final MalusFunction strategy = new MalusForFitnessFunction(constraint, LanguageHelper.lookup(configuration, "handling"), index) ; + resultingFunction.add(index, strategy); + } } return resultingFunction; diff --git a/src/core/de.evoal.core/src/main/java/de/evoal/core/main/ea/constraints/constraint/strategies/fitness/internal/MalusForFitnessFunction.java b/src/core/de.evoal.core/src/main/java/de/evoal/core/main/ea/constraints/constraint/strategies/fitness/internal/MalusForFitnessFunction.java index 9a2626e3f7e1da6d1f1de1721f36c71ae9881b0f..8d637e5fc7cf56d7b4226d519c622eea5d4ee72b 100644 --- a/src/core/de.evoal.core/src/main/java/de/evoal/core/main/ea/constraints/constraint/strategies/fitness/internal/MalusForFitnessFunction.java +++ b/src/core/de.evoal.core/src/main/java/de/evoal/core/main/ea/constraints/constraint/strategies/fitness/internal/MalusForFitnessFunction.java @@ -17,10 +17,9 @@ public class MalusForFitnessFunction implements MalusFunction { } @Override - public double apply(final Properties properties, double fitnessValue) { - // final ConstraintResult result = constraint.apply(properties); + public double apply(final Properties genoProperties, final Properties fitnessProperties, final double fitnessValue) { + final ConstraintResult result = constraint.apply(genoProperties, fitnessProperties); - throw new IllegalStateException("Not yet implemented"); -// return fitnessValue - smoothing * Math.abs(result.getComparisonDifference()); + return fitnessValue - smoothing * Math.abs(result.getComparisonDifference()); } } diff --git a/src/core/de.evoal.core/src/main/java/de/evoal/core/main/ea/constraints/constraint/utils/ConfigurationUtils.java b/src/core/de.evoal.core/src/main/java/de/evoal/core/main/ea/constraints/constraint/utils/ConfigurationUtils.java index eda85164f80e863087cecbf4e9b06f3e0a2ce23d..546a87f235c1075885ad2bba16bf619263f6e51e 100644 --- a/src/core/de.evoal.core/src/main/java/de/evoal/core/main/ea/constraints/constraint/utils/ConfigurationUtils.java +++ b/src/core/de.evoal.core/src/main/java/de/evoal/core/main/ea/constraints/constraint/utils/ConfigurationUtils.java @@ -17,8 +17,19 @@ public final class ConfigurationUtils { return handlers.getValues() .stream() .map(Instance.class::cast) - .filter(i -> "constraint-handler".equals(i.getName().getName())) - .filter(i -> name.equals(((Instance)i.findAttribute("constraint-handling").getValue()).getName().getName())) + .filter(LanguageHelper.filterInstanceByType("constraint-handler")) + .filter(LanguageHelper.filterByAttributesInstanceType("constraint-handling", name)) .collect(Collectors.toList()); } + + public static Instance findConstraintHandlerByHandlingStrategyAndCategory(final Array handlers, final String name, final String category) { + return handlers.getValues() + .stream() + .map(Instance.class::cast) + .filter(LanguageHelper.filterInstanceByType("constraint-handler")) + .filter(LanguageHelper.filterByAttributesInstanceType("constraint-handling", name)) + .filter(i -> category.equals(LanguageHelper.lookup(i, "category"))) + .findFirst() + .get(); + } } diff --git a/src/core/de.evoal.core/src/main/java/de/evoal/core/main/ea/fitness/MalusFitness.java b/src/core/de.evoal.core/src/main/java/de/evoal/core/main/ea/fitness/MalusFitness.java new file mode 100644 index 0000000000000000000000000000000000000000..9d9fd13f64729a423af21bddef9273e45a0ef7ff --- /dev/null +++ b/src/core/de.evoal.core/src/main/java/de/evoal/core/main/ea/fitness/MalusFitness.java @@ -0,0 +1,39 @@ +package de.evoal.core.main.ea.fitness; + +import de.evoal.core.api.board.Blackboard; +import de.evoal.core.api.board.BlackboardEntry; +import de.evoal.core.api.ea.constraints.strategies.fitness.MalusForFitnessStrategy; +import de.evoal.core.api.ea.fitness.FitnessDecorator; +import de.evoal.core.api.ea.fitness.FitnessFunction; +import de.evoal.core.api.properties.Properties; +import de.evoal.core.api.properties.PropertiesSpecification; +import de.evoal.core.api.utils.Requirements; +import de.evoal.languages.model.instance.*; +import lombok.extern.slf4j.Slf4j; + +import javax.enterprise.context.Dependent; +import javax.inject.Inject; +import javax.inject.Named; + +@Dependent +@Named("malus") +@Slf4j +public class MalusFitness extends FitnessDecorator { + + @Inject + @Named("optimization-output-specification") + private PropertiesSpecification fitnessSpecification; + + @Inject + private MalusForFitnessStrategy strategy; + + @Override + public double[] evaluate(final Properties properties) { + final double [] current = decoratedFunction.evaluate(properties); + final Properties fitness = new Properties(fitnessSpecification, current); + + strategy.apply(properties, fitness); + + return fitness.getValuesAsDouble(); + } +} diff --git a/src/core/de.evoal.core/src/main/java/de/evoal/core/main/ea/producer/OptimizingFunctionProducer.java b/src/core/de.evoal.core/src/main/java/de/evoal/core/main/ea/producer/OptimizingFunctionProducer.java new file mode 100644 index 0000000000000000000000000000000000000000..c199ddfb1bd194a257c748535e772ebee89ce1c8 --- /dev/null +++ b/src/core/de.evoal.core/src/main/java/de/evoal/core/main/ea/producer/OptimizingFunctionProducer.java @@ -0,0 +1,73 @@ +package de.evoal.core.main.ea.producer; + +import de.evoal.core.api.board.BlackboardEntry; +import de.evoal.core.api.cdi.ConfigurationValue; +import de.evoal.core.api.ea.fitness.FitnessFunction; +import de.evoal.core.api.properties.PropertiesSpecification; +import de.evoal.languages.model.instance.Array; +import de.evoal.languages.model.instance.Attribute; +import de.evoal.languages.model.instance.DataReference; +import de.evoal.languages.model.instance.Instance; +import org.apache.deltaspike.core.api.provider.BeanProvider; + +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.context.Dependent; +import javax.enterprise.inject.Produces; +import javax.inject.Named; + +@ApplicationScoped +public class OptimizingFunctionProducer { + @Produces + @Dependent + @Named("optimization-function-input") + public PropertiesSpecification createInputSpec(final @Named("genotype-specification") PropertiesSpecification genotypeSpec) { + return genotypeSpec; + } + + @Produces + @Dependent + @Named("optimization-function-configuration") + public Instance find(final @ConfigurationValue(entry = BlackboardEntry.EA_CONFIGURATION, access = "algorithm.fitness") Instance fitnessConfiguration) { + return findInner(fitnessConfiguration); + } + + @Produces + @Dependent + @Named("optimization-function-output") + public PropertiesSpecification createOutputSpec(final @Named("optimization-function-configuration") Instance configuration) { + return toSpecification(configuration); + } + + @Produces + @Dependent + @Named("optimization-function") + private FitnessFunction create(final @Named("optimization-function-configuration") Instance configuration) { + final String fitnessName = configuration.getName().getName(); + + return BeanProvider.getContextualReference(fitnessName, false, FitnessFunction.class) + .init(configuration); + } + + + private PropertiesSpecification toSpecification(final Instance fitnessConfiguration) { + return PropertiesSpecification.builder() + .add(((Array)findInner(fitnessConfiguration).findAttribute("maps-to").getValue()) + .getValues() + .stream() + .map(DataReference.class::cast) + .map(DataReference::getDefinition) + ) + .build(); + } + + + private static Instance findInner(final Instance fitnessConfig) { + final Attribute subFunction = fitnessConfig.findAttribute("function"); + + if(subFunction != null) { + return findInner((Instance)subFunction.getValue()); + } + + return fitnessConfig; + } +}