io.github.ericmedvet.jgea.experimenter.builders.Mappers Maven / Gradle / Ivy
Show all versions of jgea.experimenter Show documentation
/*-
* ========================LICENSE_START=================================
* jgea-experimenter
* %%
* Copyright (C) 2018 - 2024 Eric Medvet
* %%
* 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.
* =========================LICENSE_END==================================
*/
package io.github.ericmedvet.jgea.experimenter.builders;
import io.github.ericmedvet.jgea.core.InvertibleMapper;
import io.github.ericmedvet.jgea.core.representation.NamedMultivariateRealFunction;
import io.github.ericmedvet.jgea.core.representation.NamedUnivariateRealFunction;
import io.github.ericmedvet.jgea.core.representation.grammar.Chooser;
import io.github.ericmedvet.jgea.core.representation.grammar.Developer;
import io.github.ericmedvet.jgea.core.representation.grammar.grid.*;
import io.github.ericmedvet.jgea.core.representation.graph.Graph;
import io.github.ericmedvet.jgea.core.representation.graph.Node;
import io.github.ericmedvet.jgea.core.representation.graph.numeric.functiongraph.FunctionGraph;
import io.github.ericmedvet.jgea.core.representation.graph.numeric.operatorgraph.OperatorGraph;
import io.github.ericmedvet.jgea.core.representation.sequence.bit.BitString;
import io.github.ericmedvet.jgea.core.representation.sequence.integer.IntString;
import io.github.ericmedvet.jgea.core.representation.tree.Tree;
import io.github.ericmedvet.jgea.core.representation.tree.numeric.Element;
import io.github.ericmedvet.jgea.core.representation.tree.numeric.TreeBasedMultivariateRealFunction;
import io.github.ericmedvet.jgea.core.representation.tree.numeric.TreeBasedUnivariateRealFunction;
import io.github.ericmedvet.jgea.core.util.Naming;
import io.github.ericmedvet.jgea.problem.ca.MultivariateRealGridCellularAutomaton;
import io.github.ericmedvet.jnb.core.Cacheable;
import io.github.ericmedvet.jnb.core.Discoverable;
import io.github.ericmedvet.jnb.core.Param;
import io.github.ericmedvet.jnb.datastructure.DoubleRange;
import io.github.ericmedvet.jnb.datastructure.Grid;
import io.github.ericmedvet.jnb.datastructure.NumericalParametrized;
import io.github.ericmedvet.jnb.datastructure.Pair;
import io.github.ericmedvet.jsdynsym.buildable.builders.NumericalDynamicalSystems;
import io.github.ericmedvet.jsdynsym.core.composed.Stepped;
import io.github.ericmedvet.jsdynsym.core.numerical.*;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.function.DoubleUnaryOperator;
import java.util.function.Function;
import java.util.random.RandomGenerator;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
@Discoverable(prefixTemplate = "ea.mapper|m")
public class Mappers {
private Mappers() {}
@SuppressWarnings("unused")
@Cacheable
public static InvertibleMapper> bsToGrammarGrid(
@Param(value = "of", dNPM = "ea.m.identity()") InvertibleMapper beforeM,
@Param("grammar") GridGrammar grammar,
@Param(value = "l", dI = 256) int l,
@Param(value = "overwrite") boolean overwrite,
@Param(
value = "criteria",
dSs = {"least_recent", "lowest_y", "lowest_x"})
List criteria) {
Developer, GridGrammar.ReferencedGrid> gridDeveloper =
new StandardGridDeveloper<>(grammar, overwrite, criteria);
return beforeM.andThen(InvertibleMapper.from(
(eGrid, bs) -> {
Chooser> chooser = new BitStringChooser<>(bs, grammar);
return gridDeveloper.develop(chooser).orElse(eGrid);
},
eGrid -> new BitString(l),
"bsToGrammarGrid[l=%d;o=%s;c=%s]".formatted(l, overwrite, criteria)));
}
@SuppressWarnings("unused")
@Cacheable
public static InvertibleMapper, List>> dsSplit(
@Param(value = "of", dNPM = "ea.m.identity()") InvertibleMapper> beforeM) {
return beforeM.andThen(InvertibleMapper.from(
(p, ds) -> {
if (p.first().size() + p.second().size() != ds.size()) {
throw new IllegalArgumentException(
"Cannot split a double string with size %d in two double strings of sizes %d and %d"
.formatted(
ds.size(),
p.first().size(),
p.second().size()));
}
return new Pair<>(
ds.subList(0, p.first().size()),
ds.subList(p.first().size(), ds.size()));
},
p -> Stream.concat(p.first().stream(), p.second().stream()).toList(),
"dsSplit"));
}
@SuppressWarnings("unused")
@Cacheable
public static InvertibleMapper dsToBitString(
@Param(value = "of", dNPM = "ea.m.identity()") InvertibleMapper> beforeM,
@Param(value = "t", dD = 0d) double t) {
return beforeM.andThen(InvertibleMapper.from(
(eBs, ds) -> new BitString(ds.stream().map(v -> v < t).toList()),
eBs -> Collections.nCopies(eBs.size(), 0d),
"dsToBs[t=%.1f]".formatted(t)));
}
@SuppressWarnings("unused")
@Cacheable
public static InvertibleMapper> dsToFixedGrid(
@Param(value = "of", dNPM = "ea.m.identity()") InvertibleMapper> beforeM,
@Param(value = "rate", dD = 0.25) double rate,
@Param("negItem") T negItem,
@Param("posItem") T posItem) {
return beforeM.andThen(InvertibleMapper.from(
(g, ds) -> {
if (ds.size() != g.w() * g.h()) {
throw new IllegalArgumentException(
"Wrong size for the double string: %dx%d=%d expected, %d found"
.formatted(g.w(), g.h(), g.w() * g.h(), ds.size()));
}
List indexes = IntStream.range(0, ds.size())
.boxed()
.sorted(Comparator.comparingDouble(ds::get))
.limit((long) (ds.size() * rate))
.toList();
return Grid.create(
g.w(),
g.h(),
IntStream.range(0, ds.size())
.boxed()
.map(i -> indexes.contains(i) ? posItem : negItem)
.toList());
},
g -> Collections.nCopies(g.w() * g.h(), 0d),
"dsToThresholdedGrid[rate=%.2f]".formatted(rate)));
}
@SuppressWarnings("unused")
@Cacheable
public static InvertibleMapper> dsToGrammarGrid(
@Param(value = "of", dNPM = "ea.m.identity()") InvertibleMapper> beforeM,
@Param("grammar") GridGrammar grammar,
@Param(value = "l", dI = 256) int l,
@Param(value = "overwrite") boolean overwrite,
@Param(
value = "criteria",
dSs = {"least_recent", "lowest_y", "lowest_x"})
List criteria) {
Developer, GridGrammar.ReferencedGrid> gridDeveloper =
new StandardGridDeveloper<>(grammar, overwrite, criteria);
return beforeM.andThen(InvertibleMapper.from(
(eGrid, vs) -> {
Chooser> chooser = new DoublesChooser<>(vs, grammar);
return gridDeveloper.develop(chooser).orElse(eGrid);
},
eGrid -> Collections.nCopies(l, 0d),
"dsToGrammarGrid[l=%d;o=%s;c=%s]".formatted(l, overwrite, criteria)));
}
@SuppressWarnings("unused")
@Cacheable
public static InvertibleMapper dsToIs(
@Param(value = "of", dNPM = "ea.m.identity()") InvertibleMapper> beforeM,
@Param(value = "range", dNPM = "ds.range(min=-1;max=1)") DoubleRange range) {
return beforeM.andThen(InvertibleMapper.from(
(eIs, ds) -> {
DoubleRange isRange = new DoubleRange(eIs.lowerBound(), eIs.upperBound());
return new IntString(
ds.stream()
.map(v -> (int) Math.floor(isRange.denormalize(range.normalize(v))))
.map(i -> Math.max(Math.min(i, eIs.upperBound() - 1), eIs.lowerBound()))
.toList(),
eIs.lowerBound(),
eIs.upperBound());
},
eIs -> Collections.nCopies(eIs.size(), 0d),
"dsToIs[min=%.0f;max=%.0f]".formatted(range.min(), range.max())));
}
@SuppressWarnings("unused")
@Cacheable
public static & NumericalParametrized, S>
InvertibleMapper dsToNpnds(
@Param(value = "of", dNPM = "ea.m.identity()") InvertibleMapper> beforeM,
@Param("npnds") NumericalDynamicalSystems.Builder builder) {
return beforeM.andThen(InvertibleMapper.from(
(p, params) -> builder.apply(p.nOfInputs(), p.nOfOutputs())
.withParams(params.stream().mapToDouble(v -> v).toArray()),
p -> Collections.nCopies(
builder.apply(p.nOfInputs(), p.nOfOutputs()).getParams().length, 0d),
"dsToNpnds[npnds=%s]".formatted(builder)));
}
@SuppressWarnings("unused")
@Cacheable
public static InvertibleMapper> dsToThresholdedGrid(
@Param(value = "of", dNPM = "ea.m.identity()") InvertibleMapper> beforeM,
@Param(value = "t", dD = 0) double t,
@Param("negItem") T negItem,
@Param("posItem") T posItem) {
return beforeM.andThen(InvertibleMapper.from(
(g, ds) -> {
if (ds.size() != g.w() * g.h()) {
throw new IllegalArgumentException(
"Wrong size for the double string: %dx%d=%d expected, %d found"
.formatted(g.w(), g.h(), g.w() * g.h(), ds.size()));
}
return Grid.create(
g.w(),
g.h(),
ds.stream().map(d -> d > t ? posItem : negItem).toList());
},
g -> Collections.nCopies(g.w() * g.h(), 0d),
"dsToThresholdedGrid[t=%.2f]".formatted(t)));
}
@SuppressWarnings("unused")
@Cacheable
public static InvertibleMapper> enhancedNds(
@Param(value = "of", dNPM = "ea.m.identity()") InvertibleMapper> beforeM,
@Param("windowT") double windowT,
@Param(
value = "types",
dSs = {"current", "trend", "avg"})
List types) {
return beforeM.andThen(InvertibleMapper.from(
(eNds, nds) -> new EnhancedInput<>(nds, windowT, types),
eNds -> eNds,
"enhanced[wT=%.2f;%s]"
.formatted(windowT, types.stream().map(Enum::toString).collect(Collectors.joining(";")))));
}
@SuppressWarnings("unused")
@Cacheable
public static InvertibleMapper fGraphToNmrf(
@Param(value = "of", dNPM = "ea.m.identity()") InvertibleMapper> beforeM,
@Param(value = "postOperator", dNPM = "ds.f.doubleOp(activationF=identity)")
Function postOperator) {
return beforeM.andThen(InvertibleMapper.from(
(nmrf, g) -> NamedMultivariateRealFunction.from(
new FunctionGraph(g, nmrf.xVarNames(), nmrf.yVarNames()),
nmrf.xVarNames(),
nmrf.yVarNames())
.andThen(toOperator(postOperator)),
nmrf -> FunctionGraph.sampleFor(nmrf.xVarNames(), nmrf.yVarNames()),
"fGraphToNmrf[po=%s]".formatted(postOperator)));
}
@SuppressWarnings("unused")
@Cacheable
public static InvertibleMapper identity() {
return InvertibleMapper.identity();
}
@SuppressWarnings("unused")
@Cacheable
public static InvertibleMapper> isToGrammarGrid(
@Param(value = "of", dNPM = "ea.m.identity()") InvertibleMapper beforeM,
@Param("grammar") GridGrammar grammar,
@Param(value = "upperBound", dI = 16) int upperBound,
@Param(value = "l", dI = 256) int l,
@Param(value = "overwrite") boolean overwrite,
@Param(
value = "criteria",
dSs = {"least_recent", "lowest_y", "lowest_x"})
List criteria) {
Developer, GridGrammar.ReferencedGrid> gridDeveloper =
new StandardGridDeveloper<>(grammar, overwrite, criteria);
return beforeM.andThen(InvertibleMapper.from(
(eGrid, is) -> {
Chooser> chooser = new IntStringChooser<>(is, grammar);
return gridDeveloper.develop(chooser).orElse(eGrid);
},
eGrid -> new IntString(Collections.nCopies(l, 0), 0, upperBound),
"isToGrammarGrid[l=%d;o=%s;c=%s]".formatted(l, overwrite, criteria)));
}
@SuppressWarnings("unused")
@Cacheable
public static InvertibleMapper> isToGrid(
@Param(value = "of", dNPM = "ea.m.identity()") InvertibleMapper beforeM,
@Param("items") List items) {
return beforeM.andThen(InvertibleMapper.from(
(g, is) -> {
if (is.size() != g.w() * g.h()) {
throw new IllegalArgumentException(
"Wrong size for the integer string: %dx%d=%d expected, %d found"
.formatted(g.w(), g.h(), g.w() * g.h(), is.size()));
}
return Grid.create(
g.w(), g.h(), is.genes().stream().map(items::get).toList());
},
g -> new IntString(Collections.nCopies(g.w() * g.h(), 0), 0, items.size()),
"isToGrid[nOfItems=%d]".formatted(items.size())));
}
@SuppressWarnings("unused")
@Cacheable
public static InvertibleMapper multiSrTreeToNmrf(
@Param(value = "of", dNPM = "ea.m.identity()") InvertibleMapper>> beforeM,
@Param(value = "postOperator", dNPM = "ds.f.doubleOp(activationF=identity)")
Function postOperator) {
return beforeM.andThen(InvertibleMapper.from(
(nmrf, ts) -> new TreeBasedMultivariateRealFunction(ts, nmrf.xVarNames(), nmrf.yVarNames())
.andThen(toOperator(postOperator)),
nmrf -> TreeBasedMultivariateRealFunction.sampleFor(nmrf.xVarNames(), nmrf.yVarNames()),
"multiSrTreeToNmrf[po=%s]".formatted(postOperator)));
}
@SuppressWarnings("unused")
@Cacheable
public static InvertibleMapper> nmrfToGrid(
@Param(value = "of", dNPM = "ea.m.identity()") InvertibleMapper beforeM,
@Param("items") List items) {
return beforeM.andThen(InvertibleMapper.from(
(g, nmrf) -> {
if (nmrf.nOfInputs() != 2) {
throw new IllegalArgumentException(
"Wrong input size for the NMRF: 2 expected, %d found".formatted(nmrf.nOfInputs()));
}
if (nmrf.nOfOutputs() != items.size()) {
throw new IllegalArgumentException("Wrong output size for the NMRF: %d expected, %d found"
.formatted(items.size(), nmrf.nOfOutputs()));
}
return Grid.create(g.w(), g.h(), (x, y) -> {
double[] values =
nmrf.apply(new double[] {(double) x / (double) g.w(), (double) y / (double) g.h()});
return items.get(IntStream.range(0, values.length)
.boxed()
.min(Comparator.comparingDouble(i -> values[i]))
.orElse(0));
});
},
g -> NamedMultivariateRealFunction.from(
MultivariateRealFunction.from(vs -> new double[items.size()], 2, items.size()),
List.of("x", "y"),
IntStream.range(0, items.size())
.mapToObj("item%02d"::formatted)
.toList()),
"nmrfToGrid[nOfItems=%d]".formatted(items.size())));
}
@SuppressWarnings("unused")
public static InvertibleMapper nmrfToMrca(
@Param(value = "of", dNPM = "ea.m.identity()") InvertibleMapper beforeM,
@Param(value = "nOfAdditionalChannels", dI = 1) int nOfAdditionalChannels,
@Param(
value = "kernels",
dSs = {"identity", "laplacian", "sobel_edges"})
List kernels,
@Param(value = "initializer", dS = "center_all")
MultivariateRealGridCellularAutomaton.Initializer initializer,
@Param(value = "range", dNPM = "m.range(min=-1;max=1)") DoubleRange range,
@Param(value = "additiveCoefficient", dD = 1d) double additiveCoefficient,
@Param(value = "alivenessThreshold", dD = 0d) double alivenessThreshold,
@Param(value = "toroidal") boolean toroidal) {
List> kernelGrids = kernels.stream()
.map(MultivariateRealGridCellularAutomaton.Kernel::get)
.flatMap(List::stream)
.toList();
return beforeM.andThen(InvertibleMapper.from(
(mrca, nmrf) -> {
int minStateSize = MultivariateRealGridCellularAutomaton.minStateSize(mrca.getInitialStates());
int nOfInputs = (minStateSize + nOfAdditionalChannels) * kernelGrids.size();
int nOfOutputs = minStateSize + nOfAdditionalChannels;
if (nmrf.nOfInputs() != nOfInputs) {
throw new IllegalArgumentException("Wrong input size for the MRF: %d expected, %d found"
.formatted(nOfInputs, nmrf.nOfInputs()));
}
if (nmrf.nOfOutputs() != nOfOutputs) {
throw new IllegalArgumentException("Wrong output size for the MRF: %d expected, %d found"
.formatted(nOfOutputs, nmrf.nOfOutputs()));
}
return new MultivariateRealGridCellularAutomaton(
initializer.initialize(
mrca.getInitialStates().w(),
mrca.getInitialStates().h(),
minStateSize + nOfAdditionalChannels,
range),
range,
kernelGrids,
nmrf,
additiveCoefficient,
alivenessThreshold,
toroidal);
},
mrca -> {
int minStateSize = MultivariateRealGridCellularAutomaton.minStateSize(mrca.getInitialStates());
int nOfInputs = (minStateSize + nOfAdditionalChannels) * kernelGrids.size();
int nOfOutputs = minStateSize + nOfAdditionalChannels;
List varNames = MultivariateRealFunction.varNames("c", nOfOutputs);
return NamedMultivariateRealFunction.from(
MultivariateRealFunction.from(vs -> new double[nOfOutputs], nOfInputs, nOfOutputs),
IntStream.range(0, kernelGrids.size())
.mapToObj(i -> varNames.stream().map(n -> "%s_k%d".formatted(n, i)))
.flatMap(Function.identity())
.toList(),
varNames);
},
"nmrfToMrCA[addChannels=%d;kernels=%d]".formatted(nOfAdditionalChannels, kernelGrids.size())));
}
@SuppressWarnings("unused")
@Cacheable
public static InvertibleMapper> nmrfToNds(
@Param(value = "of", dNPM = "ea.m.identity()") InvertibleMapper beforeM) {
return beforeM.andThen(InvertibleMapper.from(
(nds, nmrf) -> nmrf,
nds -> NamedMultivariateRealFunction.from(
MultivariateRealFunction.from(
in -> new double[nds.nOfOutputs()], nds.nOfInputs(), nds.nOfOutputs()),
MultivariateRealFunction.varNames("i", nds.nOfInputs()),
MultivariateRealFunction.varNames("o", nds.nOfOutputs())),
"nmrfToNds"));
}
@SuppressWarnings("unused")
@Cacheable
public static InvertibleMapper nmrfToNurf(
@Param(value = "of", dNPM = "ea.m.identity()") InvertibleMapper beforeM) {
return beforeM.andThen(InvertibleMapper.from(
(nurf, nmrf) -> NamedUnivariateRealFunction.from(nmrf), nurf -> nurf, "nmrfToNurf"));
}
@SuppressWarnings("unused")
@Cacheable
public static InvertibleMapper> noisedNds(
@Param(value = "of", dNPM = "ea.m.identity()") InvertibleMapper> beforeM,
@Param(value = "inputSigma", dD = 0) double inputSigma,
@Param(value = "outputSigma", dD = 0) double outputSigma,
@Param(value = "randomGenerator", dNPM = "m.defaultRG()") RandomGenerator randomGenerator) {
return beforeM.andThen(InvertibleMapper.from(
(eNds, nds) -> new Noised<>(nds, inputSigma, outputSigma, randomGenerator),
eNds -> eNds,
"noised[in=%.2f;out=%.2f]".formatted(inputSigma, outputSigma)));
}
@SuppressWarnings("unused")
@Cacheable
public static InvertibleMapper noisedNmrf(
@Param(value = "of", dNPM = "ea.m.identity()") InvertibleMapper beforeM,
@Param(value = "sigma", dD = 0) double sigma,
@Param(value = "randomGenerator", dNPM = "m.defaultRG()") RandomGenerator randomGenerator) {
return beforeM.andThen(InvertibleMapper.from(
(eNmrf, nmrf) -> nmrf.andThen(Naming.named("noised[out=%.2f]".formatted(sigma), (DoubleUnaryOperator)
v -> v + randomGenerator.nextGaussian() * sigma)),
eNmrf -> eNmrf,
"noised[out=%.2f]".formatted(sigma)));
}
@SuppressWarnings("unused")
@Cacheable
public static InvertibleMapper ntissToNmrf(
@Param(value = "of", dNPM = "ea.m.identity()")
InvertibleMapper beforeM) {
return beforeM.andThen(InvertibleMapper.from(
(nmrf, ntiss) -> NamedMultivariateRealFunction.from(
MultivariateRealFunction.from(ntiss, nmrf.nOfInputs(), nmrf.nOfOutputs()),
nmrf.xVarNames(),
nmrf.yVarNames()),
nmrf -> MultivariateRealFunction.from(
v -> new double[nmrf.nOfOutputs()], nmrf.nOfInputs(), nmrf.nOfOutputs()),
"ntissToNmrf"));
}
@SuppressWarnings("unused")
@Cacheable
public static InvertibleMapper oGraphToNmrf(
@Param(value = "of", dNPM = "ea.m.identity()")
InvertibleMapper> beforeM,
@Param(value = "postOperator", dNPM = "ds.f.doubleOp(activationF=identity)")
Function postOperator) {
return beforeM.andThen(InvertibleMapper.from(
(nmrf, g) -> new OperatorGraph(g, nmrf.xVarNames(), nmrf.yVarNames()).andThen(toOperator(postOperator)),
nmrf -> OperatorGraph.sampleFor(nmrf.xVarNames(), nmrf.yVarNames()),
"oGraphToNmrf[po=%s]".formatted(postOperator)));
}
@SuppressWarnings("unused")
@Cacheable
public static InvertibleMapper> pair(
@Param(value = "of", dNPM = "ea.m.identity()") InvertibleMapper> beforeM,
@Param(value = "first", dNPM = "ea.m.identity()") InvertibleMapper firstM,
@Param(value = "second", dNPM = "ea.m.identity()") InvertibleMapper secondM) {
return beforeM.andThen(InvertibleMapper.from(
(p2, p1) -> new Pair<>(
firstM.mapperFor(p2.first()).apply(p1.first()),
secondM.mapperFor(p2.second()).apply(p1.second())),
p2 -> new Pair<>(firstM.exampleFor(p2.first()), secondM.exampleFor(p2.second())),
"pair[first=%s;second=%s]".formatted(firstM, secondM)));
}
@SuppressWarnings("unused")
@Cacheable
public static InvertibleMapper srTreeToNurf(
@Param(value = "of", dNPM = "ea.m.identity()") InvertibleMapper> beforeM,
@Param(value = "postOperator", dNPM = "ds.f.doubleOp(activationF=identity)")
Function postOperator) {
return beforeM.andThen(InvertibleMapper.from(
(nurf, t) -> new TreeBasedUnivariateRealFunction(t, nurf.xVarNames(), nurf.yVarName())
.andThen(toOperator(postOperator)),
nurf -> TreeBasedUnivariateRealFunction.sampleFor(nurf.xVarNames(), nurf.yVarName()),
"srTreeToNurf[po=%s]".formatted(postOperator)));
}
@SuppressWarnings("unused")
@Cacheable
public static InvertibleMapper> steppedNds(
@Param(value = "of", dNPM = "ea.m.identity()") InvertibleMapper> beforeM,
@Param(value = "stepT", dD = 1) double interval) {
return beforeM.andThen(InvertibleMapper.from(
(eNds, nds) ->
NumericalDynamicalSystem.from(new Stepped<>(nds, interval), nds.nOfInputs(), nds.nOfOutputs()),
eNds -> eNds,
"stepped[t=%.2f]".formatted(interval)));
}
private static DoubleUnaryOperator toOperator(Function f) {
return new DoubleUnaryOperator() {
@Override
public double applyAsDouble(double v) {
return f.apply(v);
}
@Override
public String toString() {
return f.toString();
}
};
}
}