diff --git a/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/AverageOptionValues.java b/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/AverageOptionValues.java index 8e3235a49..4ad33f00d 100644 --- a/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/AverageOptionValues.java +++ b/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/AverageOptionValues.java @@ -22,7 +22,7 @@ import java.util.HashMap; import java.util.Map; -class AverageOptionValues extends MapBasedOptionValues { +class AverageOptionValues extends HashMapBasedOptionValues { private final Map counts = new HashMap<>(); diff --git a/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/AverageOptionValuesFactory.java b/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/AverageOptionValuesFactory.java index 7285e2d6c..d8098aa19 100644 --- a/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/AverageOptionValuesFactory.java +++ b/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/AverageOptionValuesFactory.java @@ -22,9 +22,9 @@ import uk.ac.ox.poseidon.core.Factory; import uk.ac.ox.poseidon.core.Simulation; -public class AverageOptionValuesFactory implements Factory> { +public class AverageOptionValuesFactory implements Factory> { @Override - public OptionValues get(final Simulation simulation) { + public MutableOptionValues get(final Simulation simulation) { return new AverageOptionValues<>(); } } diff --git a/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/BestOptionsFromFriendsSupplier.java b/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/BestOptionsFromFriendsSupplier.java new file mode 100644 index 000000000..fc1019664 --- /dev/null +++ b/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/BestOptionsFromFriendsSupplier.java @@ -0,0 +1,69 @@ +/* + * POSEIDON: an agent-based model of fisheries + * Copyright (c) 2024 CoHESyS Lab cohesys.lab@gmail.com + * + * 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 3 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, see . + * + */ + +package uk.ac.ox.poseidon.agents.behaviours.choices; + +import com.google.common.collect.ImmutableList; +import ec.util.MersenneTwisterFast; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import uk.ac.ox.poseidon.agents.registers.Register; +import uk.ac.ox.poseidon.agents.vessels.Vessel; + +import java.util.Map; +import java.util.function.Supplier; + +import static com.google.common.collect.ImmutableMap.toImmutableMap; +import static uk.ac.ox.poseidon.core.MasonUtils.upToNOf; + +@RequiredArgsConstructor +class BestOptionsFromFriendsSupplier implements Supplier> { + + private final Vessel vessel; + private final int maxNumberOfFriends; + private final Register> optionValuesRegister; + private final MersenneTwisterFast rng; + + private final @Getter(lazy = true) ImmutableList friends = chooseFriends(); + + private ImmutableList chooseFriends() { + assert optionValuesRegister != null; + assert this.vessel != null; + return upToNOf( + maxNumberOfFriends, + optionValuesRegister + .getVessels() + .filter(vessel -> vessel.getHomePort() == this.vessel.getHomePort()) + .filter(vessel -> vessel != this.vessel) + .toList(), + rng + ); + } + + @Override + public OptionValues get() { + return new ImmutableOptionValues<>( + getFriends() + .stream() + .flatMap(vessel -> optionValuesRegister.get(vessel).stream()) + .flatMap(optionValues -> optionValues.getBestEntries().stream()) + .collect(toImmutableMap(Map.Entry::getKey, Map.Entry::getValue, Math::max)) + ); + } +} diff --git a/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/BestOptionsFromFriendsSupplierFactory.java b/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/BestOptionsFromFriendsSupplierFactory.java new file mode 100644 index 000000000..f17f2d557 --- /dev/null +++ b/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/BestOptionsFromFriendsSupplierFactory.java @@ -0,0 +1,56 @@ +/* + * POSEIDON: an agent-based model of fisheries + * Copyright (c) 2024 CoHESyS Lab cohesys.lab@gmail.com + * + * 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 3 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, see . + * + */ + +package uk.ac.ox.poseidon.agents.behaviours.choices; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import uk.ac.ox.poseidon.agents.registers.Register; +import uk.ac.ox.poseidon.agents.vessels.Vessel; +import uk.ac.ox.poseidon.agents.vessels.VesselScopeFactory; +import uk.ac.ox.poseidon.core.Factory; +import uk.ac.ox.poseidon.core.Simulation; + +import java.util.function.Supplier; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class BestOptionsFromFriendsSupplierFactory + extends VesselScopeFactory>> { + + private int maxNumberOfFriends; + private Factory>> optionValuesRegister; + + @Override + protected Supplier> newInstance( + final Simulation simulation, + final Vessel vessel + ) { + return new BestOptionsFromFriendsSupplier<>( + vessel, + maxNumberOfFriends, + optionValuesRegister.get(simulation), + simulation.random + ); + } +} diff --git a/agents/src/main/java/uk/ac/ox/poseidon/agents/registers/TransformedRegister.java b/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/BestOptionsSupplier.java similarity index 50% rename from agents/src/main/java/uk/ac/ox/poseidon/agents/registers/TransformedRegister.java rename to agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/BestOptionsSupplier.java index 11e868f9b..10f9c14b2 100644 --- a/agents/src/main/java/uk/ac/ox/poseidon/agents/registers/TransformedRegister.java +++ b/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/BestOptionsSupplier.java @@ -17,39 +17,31 @@ * */ -package uk.ac.ox.poseidon.agents.registers; +package uk.ac.ox.poseidon.agents.behaviours.choices; import lombok.RequiredArgsConstructor; +import uk.ac.ox.poseidon.agents.registers.Register; import uk.ac.ox.poseidon.agents.vessels.Vessel; -import java.util.Map.Entry; -import java.util.Optional; -import java.util.function.Function; -import java.util.stream.Stream; +import java.util.Map; +import java.util.function.Supplier; -import static java.util.Map.entry; +import static com.google.common.collect.ImmutableMap.toImmutableMap; @RequiredArgsConstructor -public class TransformedRegister implements Register { +public class BestOptionsSupplier implements Supplier> { - private final Register sourceRegister; - private final Function>, Stream>> transformer; + private final Vessel vessel; + private final Register> optionValuesRegister; @Override - public Optional get(final Vessel vessel) { - final Optional> sourceEntry = - sourceRegister - .get(vessel) - .map(value -> entry(vessel, value)); - return transformer - .apply(sourceEntry.stream()) - .findAny() - .map(Entry::getValue); + public OptionValues get() { + return new ImmutableOptionValues<>( + optionValuesRegister + .getOtherEntries(vessel) + .map(Map.Entry::getValue) + .flatMap(optionValues -> optionValues.getBestEntries().stream()) + .collect(toImmutableMap(Map.Entry::getKey, Map.Entry::getValue, Math::max)) + ); } - - @Override - public Stream> getAllEntries() { - return transformer.apply(sourceRegister.getAllEntries()); - } - } diff --git a/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/BestOptionsRegisterFactory.java b/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/BestOptionsSupplierFactory.java similarity index 66% rename from agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/BestOptionsRegisterFactory.java rename to agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/BestOptionsSupplierFactory.java index 1175e0200..d2d774454 100644 --- a/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/BestOptionsRegisterFactory.java +++ b/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/BestOptionsSupplierFactory.java @@ -23,31 +23,31 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -import one.util.streamex.EntryStream; import uk.ac.ox.poseidon.agents.registers.Register; -import uk.ac.ox.poseidon.agents.registers.TransformedRegister; +import uk.ac.ox.poseidon.agents.vessels.Vessel; +import uk.ac.ox.poseidon.agents.vessels.VesselScopeFactory; import uk.ac.ox.poseidon.core.Factory; import uk.ac.ox.poseidon.core.Simulation; -import uk.ac.ox.poseidon.core.SimulationScopeFactory; -import java.util.Map.Entry; +import java.util.function.Supplier; @Getter @Setter @NoArgsConstructor @AllArgsConstructor -public class BestOptionsRegisterFactory - extends SimulationScopeFactory>> { +public class BestOptionsSupplierFactory + extends VesselScopeFactory>> { Factory>> optionValuesRegister; @Override - protected Register> newInstance(final Simulation simulation) { - return new TransformedRegister<>( - optionValuesRegister.get(simulation), - entryStream -> EntryStream - .of(entryStream) - .flatMapValues(optionValues -> optionValues.getBestEntries().stream()) + protected Supplier> newInstance( + final Simulation simulation, + final Vessel vessel + ) { + return new BestOptionsSupplier<>( + vessel, + optionValuesRegister.get(simulation) ); } } diff --git a/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/EpsilonGreedyChooser.java b/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/EpsilonGreedyChooser.java index 2edbdce02..694f651f1 100644 --- a/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/EpsilonGreedyChooser.java +++ b/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/EpsilonGreedyChooser.java @@ -30,7 +30,7 @@ public class EpsilonGreedyChooser implements Supplier { private final double epsilon; - private final OptionValues optionValues; + private final MutableOptionValues optionValues; private final Picker explorer; private final Picker exploiter; private final Evaluator evaluator; @@ -40,7 +40,7 @@ public class EpsilonGreedyChooser implements Supplier { public EpsilonGreedyChooser( final double epsilon, - final OptionValues optionValues, + final MutableOptionValues optionValues, final Picker explorer, final Picker exploiter, final Evaluator evaluator, diff --git a/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/ExponentialMovingAverageOptionValues.java b/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/ExponentialMovingAverageOptionValues.java index cae4cf8e4..d3030d6c9 100644 --- a/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/ExponentialMovingAverageOptionValues.java +++ b/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/ExponentialMovingAverageOptionValues.java @@ -21,7 +21,7 @@ import static uk.ac.ox.poseidon.core.utils.Preconditions.checkUnitRange; -class ExponentialMovingAverageOptionValues extends MapBasedOptionValues { +class ExponentialMovingAverageOptionValues extends HashMapBasedOptionValues { private final double alpha; diff --git a/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/ExponentialMovingAverageOptionValuesFactory.java b/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/ExponentialMovingAverageOptionValuesFactory.java index fd9481e26..9374397c1 100644 --- a/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/ExponentialMovingAverageOptionValuesFactory.java +++ b/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/ExponentialMovingAverageOptionValuesFactory.java @@ -31,12 +31,12 @@ @Setter @NoArgsConstructor @AllArgsConstructor -public class ExponentialMovingAverageOptionValuesFactory extends VesselScopeFactory> { +public class ExponentialMovingAverageOptionValuesFactory extends VesselScopeFactory> { private double alpha; @Override - protected OptionValues newInstance( + protected MutableOptionValues newInstance( final Simulation simulation, final Vessel vessel ) { diff --git a/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/HashMapBasedOptionValues.java b/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/HashMapBasedOptionValues.java new file mode 100644 index 000000000..66377b73a --- /dev/null +++ b/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/HashMapBasedOptionValues.java @@ -0,0 +1,53 @@ +/* + * POSEIDON: an agent-based model of fisheries + * Copyright (c) 2024 CoHESyS Lab cohesys.lab@gmail.com + * + * 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 3 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, see . + * + */ + +package uk.ac.ox.poseidon.agents.behaviours.choices; + +import lombok.Getter; + +import java.util.HashMap; +import java.util.Map; + +@Getter +public abstract class HashMapBasedOptionValues + extends MapBasedOptionValues + implements MutableOptionValues { + + protected final Map values = new HashMap<>(); + + @Override + public void observe( + final O option, + final double value + ) { + final double oldValue = values.getOrDefault(option, 0.0); + values.put(option, newValue(option, oldValue, value)); + invalidateCache(); + } + + protected void invalidateCache() { + cachedBest = null; + } + + protected abstract double newValue( + O option, + double oldValue, + double observedValue + ); +} diff --git a/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/ImmutableOptionValues.java b/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/ImmutableOptionValues.java new file mode 100644 index 000000000..f5a3cc330 --- /dev/null +++ b/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/ImmutableOptionValues.java @@ -0,0 +1,36 @@ +/* + * POSEIDON: an agent-based model of fisheries + * Copyright (c) 2024 CoHESyS Lab cohesys.lab@gmail.com + * + * 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 3 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, see . + * + */ + +package uk.ac.ox.poseidon.agents.behaviours.choices; + +import com.google.common.collect.ImmutableMap; +import lombok.Getter; + +import java.util.Map; + +@Getter +public class ImmutableOptionValues extends MapBasedOptionValues { + + private final ImmutableMap values; + + public ImmutableOptionValues(final Map values) { + this.values = ImmutableMap.copyOf(values); + } + +} diff --git a/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/MapBasedOptionValues.java b/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/MapBasedOptionValues.java index d370fe988..e367853a0 100644 --- a/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/MapBasedOptionValues.java +++ b/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/MapBasedOptionValues.java @@ -21,7 +21,6 @@ import ec.util.MersenneTwisterFast; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; @@ -32,12 +31,14 @@ import static uk.ac.ox.poseidon.core.MasonUtils.oneOf; public abstract class MapBasedOptionValues implements OptionValues { - protected final Map values = new HashMap<>(); - private List> cachedBest = null; + + protected List> cachedBest = null; + + protected abstract Map getValues(); @Override public Optional getValue(final O option) { - return Optional.ofNullable(values.get(option)); + return Optional.ofNullable(getValues().get(option)); } @Override @@ -58,7 +59,7 @@ public Optional getBestValue() { @Override public List> getBestEntries() { if (cachedBest == null) { - cachedBest = values + cachedBest = getValues() .entrySet() .stream() .collect(maxAll(comparingByValue(), toImmutableList())); @@ -74,23 +75,4 @@ public Optional> getBestEntry(final MersenneTwisterFast rng : Optional.of(oneOf(bestEntries, rng)); } - protected void invalidateCache() { - cachedBest = null; - } - - @Override - public void observe( - final O option, - final double value - ) { - final double oldValue = values.getOrDefault(option, 0.0); - values.put(option, newValue(option, oldValue, value)); - invalidateCache(); - } - - protected abstract double newValue( - O option, - double oldValue, - double observedValue - ); } diff --git a/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/MutableOptionValues.java b/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/MutableOptionValues.java new file mode 100644 index 000000000..6e2dde01e --- /dev/null +++ b/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/MutableOptionValues.java @@ -0,0 +1,27 @@ +/* + * POSEIDON: an agent-based model of fisheries + * Copyright (c) 2024 CoHESyS Lab cohesys.lab@gmail.com + * + * 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 3 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, see . + * + */ + +package uk.ac.ox.poseidon.agents.behaviours.choices; + +public interface MutableOptionValues extends OptionValues { + void observe( + O option, + double value + ); +} diff --git a/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/OptionValues.java b/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/OptionValues.java index f85e3a921..8b453ad9a 100644 --- a/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/OptionValues.java +++ b/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/choices/OptionValues.java @@ -27,11 +27,6 @@ public interface OptionValues { - void observe( - O option, - double value - ); - Optional getValue(O option); List getBestOptions(); diff --git a/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/destination/EpsilonGreedyDestinationSupplierFactory.java b/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/destination/EpsilonGreedyDestinationSupplierFactory.java index 2b0d9e74f..ca9efcf9b 100644 --- a/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/destination/EpsilonGreedyDestinationSupplierFactory.java +++ b/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/destination/EpsilonGreedyDestinationSupplierFactory.java @@ -26,7 +26,7 @@ import sim.util.Int2D; import uk.ac.ox.poseidon.agents.behaviours.choices.EpsilonGreedyChooser; import uk.ac.ox.poseidon.agents.behaviours.choices.Evaluator; -import uk.ac.ox.poseidon.agents.behaviours.choices.OptionValues; +import uk.ac.ox.poseidon.agents.behaviours.choices.MutableOptionValues; import uk.ac.ox.poseidon.agents.behaviours.choices.Picker; import uk.ac.ox.poseidon.agents.vessels.Vessel; import uk.ac.ox.poseidon.agents.vessels.VesselScopeFactory; @@ -39,7 +39,7 @@ public class EpsilonGreedyDestinationSupplierFactory extends VesselScopeFactory { private double epsilon; - private VesselScopeFactory> optionValues; + private VesselScopeFactory> optionValues; private VesselScopeFactory> explorer; private VesselScopeFactory> exploiter; private VesselScopeFactory> destinationEvaluator; diff --git a/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/destination/ImitatingPicker.java b/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/destination/ImitatingPicker.java index d80fa9d31..b294cfaaa 100644 --- a/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/destination/ImitatingPicker.java +++ b/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/destination/ImitatingPicker.java @@ -23,17 +23,13 @@ import lombok.RequiredArgsConstructor; import uk.ac.ox.poseidon.agents.behaviours.choices.OptionValues; import uk.ac.ox.poseidon.agents.behaviours.choices.Picker; -import uk.ac.ox.poseidon.agents.registers.Register; import java.util.List; import java.util.Map.Entry; import java.util.Optional; +import java.util.function.Supplier; import static java.lang.Double.NEGATIVE_INFINITY; -import static java.util.Map.Entry.comparingByValue; -import static java.util.stream.Collectors.mapping; -import static java.util.stream.Collectors.toList; -import static one.util.streamex.MoreCollectors.maxAll; import static uk.ac.ox.poseidon.core.MasonUtils.upToOneOf; /** @@ -50,7 +46,7 @@ public class ImitatingPicker implements Picker { private final OptionValues optionValues; - private final Register> candidateRegister; + private final Supplier> candidatesSupplier; private final MersenneTwisterFast rng; @Override @@ -62,11 +58,14 @@ public Optional pick() { final double currentBestValue = currentBestEntry.map(Entry::getValue).orElse(NEGATIVE_INFINITY); - final List candidates = candidateRegister - .getAllEntries() - .map(Entry::getValue) - .filter(entry -> entry.getValue() > currentBestValue) - .collect(maxAll(comparingByValue(), mapping(Entry::getKey, toList()))); + final List candidates = + candidatesSupplier + .get() + .getBestEntries() + .stream() + .filter(entry -> entry.getValue() > currentBestValue) + .map(Entry::getKey) + .toList(); return upToOneOf(candidates, rng) .or(() -> currentBestEntry.map(Entry::getKey)); diff --git a/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/destination/ImitatingPickerFactory.java b/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/destination/ImitatingPickerFactory.java index 535c398ea..e91351cda 100644 --- a/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/destination/ImitatingPickerFactory.java +++ b/agents/src/main/java/uk/ac/ox/poseidon/agents/behaviours/destination/ImitatingPickerFactory.java @@ -24,13 +24,11 @@ import lombok.NoArgsConstructor; import lombok.Setter; import uk.ac.ox.poseidon.agents.behaviours.choices.OptionValues; -import uk.ac.ox.poseidon.agents.registers.Register; import uk.ac.ox.poseidon.agents.vessels.Vessel; import uk.ac.ox.poseidon.agents.vessels.VesselScopeFactory; -import uk.ac.ox.poseidon.core.Factory; import uk.ac.ox.poseidon.core.Simulation; -import java.util.Map.Entry; +import java.util.function.Supplier; @Getter @Setter @@ -39,7 +37,7 @@ public class ImitatingPickerFactory extends VesselScopeFactory> { private VesselScopeFactory> optionValues; - private Factory>> candidateRegister; + private VesselScopeFactory>> optionValuesSupplier; @Override protected ImitatingPicker newInstance( @@ -48,7 +46,7 @@ protected ImitatingPicker newInstance( ) { return new ImitatingPicker<>( optionValues.get(simulation, vessel), - candidateRegister.get(simulation), + optionValuesSupplier.get(simulation, vessel), simulation.random ); } diff --git a/agents/src/main/java/uk/ac/ox/poseidon/agents/registers/MutableRegister.java b/agents/src/main/java/uk/ac/ox/poseidon/agents/registers/HashMapRegister.java similarity index 89% rename from agents/src/main/java/uk/ac/ox/poseidon/agents/registers/MutableRegister.java rename to agents/src/main/java/uk/ac/ox/poseidon/agents/registers/HashMapRegister.java index 5edc3e7cf..c7eec9436 100644 --- a/agents/src/main/java/uk/ac/ox/poseidon/agents/registers/MutableRegister.java +++ b/agents/src/main/java/uk/ac/ox/poseidon/agents/registers/HashMapRegister.java @@ -27,7 +27,7 @@ import java.util.function.Function; import java.util.stream.Stream; -public class MutableRegister implements Register { +public class HashMapRegister implements Register { private final WeakHashMap map = new WeakHashMap<>(); @@ -36,11 +36,17 @@ public Optional get(final Vessel vessel) { return Optional.ofNullable(map.get(vessel)); } + @Override + public Stream getVessels() { + return map.keySet().stream(); + } + @Override public Stream> getAllEntries() { return map.entrySet().stream(); } + @Override public T computeIfAbsent( final Vessel vessel, final Function mappingFunction diff --git a/agents/src/main/java/uk/ac/ox/poseidon/agents/registers/Register.java b/agents/src/main/java/uk/ac/ox/poseidon/agents/registers/Register.java index 27b549986..e6bd0c172 100644 --- a/agents/src/main/java/uk/ac/ox/poseidon/agents/registers/Register.java +++ b/agents/src/main/java/uk/ac/ox/poseidon/agents/registers/Register.java @@ -23,16 +23,23 @@ import java.util.Map.Entry; import java.util.Optional; +import java.util.function.Function; import java.util.stream.Stream; public interface Register { Optional get(Vessel vessel); + Stream getVessels(); + Stream> getAllEntries(); default Stream> getOtherEntries(final Vessel vessel) { return getAllEntries().filter(entry -> !entry.getKey().equals(vessel)); } - + + T computeIfAbsent( + Vessel vessel, + Function mappingFunction + ); } diff --git a/agents/src/main/java/uk/ac/ox/poseidon/agents/registers/MutableRegisterFactory.java b/agents/src/main/java/uk/ac/ox/poseidon/agents/registers/RegisterFactory.java similarity index 82% rename from agents/src/main/java/uk/ac/ox/poseidon/agents/registers/MutableRegisterFactory.java rename to agents/src/main/java/uk/ac/ox/poseidon/agents/registers/RegisterFactory.java index 71772b7a8..14189a993 100644 --- a/agents/src/main/java/uk/ac/ox/poseidon/agents/registers/MutableRegisterFactory.java +++ b/agents/src/main/java/uk/ac/ox/poseidon/agents/registers/RegisterFactory.java @@ -22,10 +22,10 @@ import uk.ac.ox.poseidon.core.Simulation; import uk.ac.ox.poseidon.core.SimulationScopeFactory; -public class MutableRegisterFactory extends SimulationScopeFactory> { +public class RegisterFactory extends SimulationScopeFactory> { @Override - protected MutableRegister newInstance(final Simulation simulation) { - return new MutableRegister<>(); + protected HashMapRegister newInstance(final Simulation simulation) { + return new HashMapRegister<>(); } } diff --git a/agents/src/main/java/uk/ac/ox/poseidon/agents/registers/RegisteringFactory.java b/agents/src/main/java/uk/ac/ox/poseidon/agents/registers/RegisteringFactory.java index 40e45c4cb..cebe20d63 100644 --- a/agents/src/main/java/uk/ac/ox/poseidon/agents/registers/RegisteringFactory.java +++ b/agents/src/main/java/uk/ac/ox/poseidon/agents/registers/RegisteringFactory.java @@ -34,7 +34,7 @@ @AllArgsConstructor public class RegisteringFactory extends VesselScopeFactory { - private Factory> register; + private Factory> register; private VesselScopeFactory delegate; @Override diff --git a/core/src/main/java/uk/ac/ox/poseidon/core/MasonUtils.java b/core/src/main/java/uk/ac/ox/poseidon/core/MasonUtils.java index 003bbd02f..04bd3c596 100644 --- a/core/src/main/java/uk/ac/ox/poseidon/core/MasonUtils.java +++ b/core/src/main/java/uk/ac/ox/poseidon/core/MasonUtils.java @@ -19,6 +19,7 @@ package uk.ac.ox.poseidon.core; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import ec.util.MersenneTwisterFast; import sim.field.continuous.Continuous2D; @@ -36,6 +37,7 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.ImmutableSet.toImmutableSet; +import static uk.ac.ox.poseidon.core.utils.Preconditions.checkNonNegative; public class MasonUtils { @@ -107,6 +109,28 @@ public static Optional upToOneOf( .map(xs -> xs.get(oneOfIndices(candidates, random))); } + public static ImmutableList upToNOf( + final int n, + final List candidates, + final MersenneTwisterFast rng + ) { + checkNonNegative(n, "n"); + if (n == 0 || candidates.isEmpty()) return ImmutableList.of(); + final int size = candidates.size(); + if (n < candidates.size()) return ImmutableList.copyOf(candidates); + final ImmutableList.Builder builder = ImmutableList.builder(); + int i = 0; + int j = 0; + while (j < n && i < size) { + if (rng.nextInt(size - i) < n - j) { + builder.add(candidates.get(i)); + j += 1; + } + i += 1; + } + return builder.build(); + } + public static boolean inBounds( final Double2D location, final Continuous2D continuous2D diff --git a/core/src/main/java/uk/ac/ox/poseidon/core/utils/Preconditions.java b/core/src/main/java/uk/ac/ox/poseidon/core/utils/Preconditions.java index 2d94bbff2..f219d6a34 100644 --- a/core/src/main/java/uk/ac/ox/poseidon/core/utils/Preconditions.java +++ b/core/src/main/java/uk/ac/ox/poseidon/core/utils/Preconditions.java @@ -33,6 +33,17 @@ public static double checkUnitRange( return value; } + public static int checkNonNegative( + final int value, + final String name + ) { + checkArgument( + value >= 0, + name + " must be not be negative but was " + value + ); + return value; + } + public static double checkNonNegative( final double value, final String name diff --git a/examples/src/main/java/uk/ac/ox/poseidon/examples/BasicScenario.java b/examples/src/main/java/uk/ac/ox/poseidon/examples/BasicScenario.java index 2fcdc0fad..61b36c406 100644 --- a/examples/src/main/java/uk/ac/ox/poseidon/examples/BasicScenario.java +++ b/examples/src/main/java/uk/ac/ox/poseidon/examples/BasicScenario.java @@ -25,9 +25,9 @@ import sim.util.Int2D; import uk.ac.ox.poseidon.agents.behaviours.BackToInitialBehaviourFactory; import uk.ac.ox.poseidon.agents.behaviours.WaitBehaviourFactory; -import uk.ac.ox.poseidon.agents.behaviours.choices.BestOptionsRegisterFactory; +import uk.ac.ox.poseidon.agents.behaviours.choices.BestOptionsFromFriendsSupplierFactory; import uk.ac.ox.poseidon.agents.behaviours.choices.ExponentialMovingAverageOptionValuesFactory; -import uk.ac.ox.poseidon.agents.behaviours.choices.OptionValues; +import uk.ac.ox.poseidon.agents.behaviours.choices.MutableOptionValues; import uk.ac.ox.poseidon.agents.behaviours.destination.*; import uk.ac.ox.poseidon.agents.behaviours.fishing.DefaultFishingBehaviourFactory; import uk.ac.ox.poseidon.agents.behaviours.travel.TravelAlongPathBehaviourFactory; @@ -36,8 +36,8 @@ import uk.ac.ox.poseidon.agents.fisheables.CurrentCellFisheableFactory; import uk.ac.ox.poseidon.agents.fleets.DefaultFleetFactory; import uk.ac.ox.poseidon.agents.fleets.Fleet; -import uk.ac.ox.poseidon.agents.registers.MutableRegister; -import uk.ac.ox.poseidon.agents.registers.MutableRegisterFactory; +import uk.ac.ox.poseidon.agents.registers.Register; +import uk.ac.ox.poseidon.agents.registers.RegisterFactory; import uk.ac.ox.poseidon.agents.registers.RegisteringFactory; import uk.ac.ox.poseidon.agents.vessels.RandomHomePortFactory; import uk.ac.ox.poseidon.agents.vessels.VesselFactory; @@ -79,8 +79,8 @@ @Setter public class BasicScenario extends Scenario { - private Factory>> optionValuesRegister = - new MutableRegisterFactory<>(); + private Factory>> optionValuesRegister = + new RegisterFactory<>(); private Factory speciesA = new SpeciesFactory("A"); private Factory speciesB = new SpeciesFactory("B"); private Factory biomassDiffusionRule = @@ -185,7 +185,7 @@ public class BasicScenario extends Scenario { ), 0 ); - private VesselScopeFactory> optionValues = + private VesselScopeFactory> optionValues = new RegisteringFactory<>( optionValuesRegister, new ExponentialMovingAverageOptionValuesFactory<>(0.5) @@ -212,7 +212,8 @@ public class BasicScenario extends Scenario { ), new ImitatingPickerFactory<>( optionValues, - new BestOptionsRegisterFactory<>( + new BestOptionsFromFriendsSupplierFactory<>( + 2, optionValuesRegister ) ),