From 86f00ee06a93af3bfde10ff5cefb07ff91d22f43 Mon Sep 17 00:00:00 2001 From: Alexandre <alexandre.perles@gmail.com> Date: Thu, 1 Jun 2023 15:34:19 +0200 Subject: [PATCH] Clean code and structure --- .idea/.name | 1 + settings.gradle | 2 +- src/main/java/fr/irit/smac/amas/Cluster.java | 33 ------------ .../java/fr/irit/smac/amas/DataPoint.java | 5 -- .../java/fr/irit/smac/amas/MASSettings.java | 7 --- .../irit/smac/amas/SimilarityScoreMethod.java | 8 --- .../controller/ClusterAMASController.java | 47 ---------------- .../smac/amas/controller/command/Command.java | 4 -- .../command/NewDataPointCommand.java | 8 --- .../amas/controller/command/SolveCommand.java | 4 -- .../smac/amas/controller/query/Query.java | 4 -- .../java/fr/irit/smac/clumate/CluMATE.java | 26 +++++++++ .../smac/{ => clumate}/amas/AMASOption.java | 2 +- .../smac/{ => clumate}/amas/ClusterAMAS.java | 14 +++-- .../smac/{ => clumate}/amas/ClusterAgent.java | 44 ++++++++------- .../amas/ClusterEnvironment.java | 13 ++--- .../irit/smac/clumate/amas/MASSettings.java | 11 ++++ .../amas/controller/CluMATEController.java | 51 ++++++++++++++++++ .../amas/controller/command/Command.java | 4 ++ .../command/NewDataPointCommand.java | 8 +++ .../controller/command/ShutdownCommand.java | 2 +- .../amas/controller/command/SolveCommand.java | 4 ++ .../clumate/amas/controller/query/Query.java | 4 ++ .../clumate/amas/controller/query/Result.java | 20 +++++++ .../query/RetrieveClustersResultQuery.java | 2 +- .../amas/messages/EvaluatedScoreMessage.java | 2 +- .../messages/RequestSimilarityMessage.java | 4 +- .../fr/irit/smac/clumate/cluster/Cluster.java | 21 ++++++++ .../irit/smac/clumate/cluster/DataPoint.java | 4 ++ .../smac/clumate/cluster/DataPointFuser.java | 6 +++ .../cluster/SimilarityScoreMethod.java | 10 ++++ src/test/groovy/SimpleClusteringIT.groovy | 44 +++++++++++++++ src/test/groovy/SimpleClusteringTest.groovy | 54 ------------------- 33 files changed, 260 insertions(+), 213 deletions(-) create mode 100644 .idea/.name delete mode 100644 src/main/java/fr/irit/smac/amas/Cluster.java delete mode 100644 src/main/java/fr/irit/smac/amas/DataPoint.java delete mode 100644 src/main/java/fr/irit/smac/amas/MASSettings.java delete mode 100644 src/main/java/fr/irit/smac/amas/SimilarityScoreMethod.java delete mode 100644 src/main/java/fr/irit/smac/amas/controller/ClusterAMASController.java delete mode 100644 src/main/java/fr/irit/smac/amas/controller/command/Command.java delete mode 100644 src/main/java/fr/irit/smac/amas/controller/command/NewDataPointCommand.java delete mode 100644 src/main/java/fr/irit/smac/amas/controller/command/SolveCommand.java delete mode 100644 src/main/java/fr/irit/smac/amas/controller/query/Query.java create mode 100644 src/main/java/fr/irit/smac/clumate/CluMATE.java rename src/main/java/fr/irit/smac/{ => clumate}/amas/AMASOption.java (50%) rename src/main/java/fr/irit/smac/{ => clumate}/amas/ClusterAMAS.java (78%) rename src/main/java/fr/irit/smac/{ => clumate}/amas/ClusterAgent.java (83%) rename src/main/java/fr/irit/smac/{ => clumate}/amas/ClusterEnvironment.java (66%) create mode 100644 src/main/java/fr/irit/smac/clumate/amas/MASSettings.java create mode 100644 src/main/java/fr/irit/smac/clumate/amas/controller/CluMATEController.java create mode 100644 src/main/java/fr/irit/smac/clumate/amas/controller/command/Command.java create mode 100644 src/main/java/fr/irit/smac/clumate/amas/controller/command/NewDataPointCommand.java rename src/main/java/fr/irit/smac/{ => clumate}/amas/controller/command/ShutdownCommand.java (50%) create mode 100644 src/main/java/fr/irit/smac/clumate/amas/controller/command/SolveCommand.java create mode 100644 src/main/java/fr/irit/smac/clumate/amas/controller/query/Query.java create mode 100644 src/main/java/fr/irit/smac/clumate/amas/controller/query/Result.java rename src/main/java/fr/irit/smac/{ => clumate}/amas/controller/query/RetrieveClustersResultQuery.java (55%) rename src/main/java/fr/irit/smac/{ => clumate}/amas/messages/EvaluatedScoreMessage.java (89%) rename src/main/java/fr/irit/smac/{ => clumate}/amas/messages/RequestSimilarityMessage.java (77%) create mode 100644 src/main/java/fr/irit/smac/clumate/cluster/Cluster.java create mode 100644 src/main/java/fr/irit/smac/clumate/cluster/DataPoint.java create mode 100644 src/main/java/fr/irit/smac/clumate/cluster/DataPointFuser.java create mode 100644 src/main/java/fr/irit/smac/clumate/cluster/SimilarityScoreMethod.java create mode 100644 src/test/groovy/SimpleClusteringIT.groovy delete mode 100644 src/test/groovy/SimpleClusteringTest.groovy diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..64fea47 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +CluMATE \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 540fc76..af11a34 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,4 +1,4 @@ -rootProject.name = 'Mascl' +rootProject.name = 'CluMATE' // Uncomment the two next lines when building amak locally include ':amak' diff --git a/src/main/java/fr/irit/smac/amas/Cluster.java b/src/main/java/fr/irit/smac/amas/Cluster.java deleted file mode 100644 index ad97a75..0000000 --- a/src/main/java/fr/irit/smac/amas/Cluster.java +++ /dev/null @@ -1,33 +0,0 @@ -package fr.irit.smac.amas; - -import lombok.Getter; -import lombok.ToString; - -@ToString -public class Cluster { - @Getter - private final DataPoint representative; - @Getter - private final int size; - - public Cluster(DataPoint representative) { - this(representative, 1); - } - - public Cluster(DataPoint representative, int size) { - this.representative = representative; - this.size = size; - } - - public Cluster fuse(Cluster other) { - var newRepresentative = representative.fuse(other.representative); - var newSize = size + other.size; - return new Cluster(newRepresentative, newSize); - } - - public Cluster fuse(DataPoint other) { - var newRepresentative = representative.fuse(other); - var newSize = size + 1; - return new Cluster(newRepresentative, newSize); - } -} diff --git a/src/main/java/fr/irit/smac/amas/DataPoint.java b/src/main/java/fr/irit/smac/amas/DataPoint.java deleted file mode 100644 index 63f767e..0000000 --- a/src/main/java/fr/irit/smac/amas/DataPoint.java +++ /dev/null @@ -1,5 +0,0 @@ -package fr.irit.smac.amas; - -public interface DataPoint { - DataPoint fuse(DataPoint representative); -} diff --git a/src/main/java/fr/irit/smac/amas/MASSettings.java b/src/main/java/fr/irit/smac/amas/MASSettings.java deleted file mode 100644 index ebb31a8..0000000 --- a/src/main/java/fr/irit/smac/amas/MASSettings.java +++ /dev/null @@ -1,7 +0,0 @@ -package fr.irit.smac.amas; - -import java.util.EnumSet; - -public record MASSettings(SimilarityScoreMethod similarityScoreMethod, float fusionThreshold, EnumSet<AMASOption> amasOptions) { - -} diff --git a/src/main/java/fr/irit/smac/amas/SimilarityScoreMethod.java b/src/main/java/fr/irit/smac/amas/SimilarityScoreMethod.java deleted file mode 100644 index f2d3b3c..0000000 --- a/src/main/java/fr/irit/smac/amas/SimilarityScoreMethod.java +++ /dev/null @@ -1,8 +0,0 @@ -package fr.irit.smac.amas; - - -public interface SimilarityScoreMethod { - float apply(DataPoint dp1, DataPoint dp2); - - boolean areRoughlySimilar(DataPoint dp1, DataPoint dp2); -} diff --git a/src/main/java/fr/irit/smac/amas/controller/ClusterAMASController.java b/src/main/java/fr/irit/smac/amas/controller/ClusterAMASController.java deleted file mode 100644 index e42553f..0000000 --- a/src/main/java/fr/irit/smac/amas/controller/ClusterAMASController.java +++ /dev/null @@ -1,47 +0,0 @@ -package fr.irit.smac.amas.controller; - -import fr.irit.smac.amak.scheduling.Scheduler; -import fr.irit.smac.amas.*; -import fr.irit.smac.amas.controller.command.NewDataPointCommand; -import fr.irit.smac.amas.controller.command.ShutdownCommand; -import fr.irit.smac.amas.controller.command.SolveCommand; -import fr.irit.smac.amas.controller.query.RetrieveClustersResultQuery; -import lombok.Getter; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.Executors; - -public class ClusterAMASController { - @Getter - private final ClusterEnvironment environment; - @Getter - private final MASSettings masSettings; - @Getter - private final ClusterAMAS amas; - @Getter - private final Scheduler scheduler; - - public ClusterAMASController(MASSettings masSettings) { - this.masSettings = masSettings; - environment = new ClusterEnvironment(); - amas = new ClusterAMAS(environment, masSettings); - scheduler = new Scheduler(Executors.newSingleThreadExecutor(), amas, environment); - } - - public void handle(NewDataPointCommand newDataPointCommand) { - environment.addDataPoints(newDataPointCommand.newDataPoints()); - } - - public void handle(SolveCommand solveCommand) { - scheduler.startWithSleepSync(0); - } - - public void handle(ShutdownCommand shutdownCommand) { - scheduler.stop(); - } - - public List<Cluster> handle(RetrieveClustersResultQuery retrieveClustersResultQuery) { - return amas.getAgents(ClusterAgent.class).stream().map(agent -> agent.getCluster()).toList(); - } -} diff --git a/src/main/java/fr/irit/smac/amas/controller/command/Command.java b/src/main/java/fr/irit/smac/amas/controller/command/Command.java deleted file mode 100644 index a9a6785..0000000 --- a/src/main/java/fr/irit/smac/amas/controller/command/Command.java +++ /dev/null @@ -1,4 +0,0 @@ -package fr.irit.smac.amas.controller.command; - -public interface Command { -} diff --git a/src/main/java/fr/irit/smac/amas/controller/command/NewDataPointCommand.java b/src/main/java/fr/irit/smac/amas/controller/command/NewDataPointCommand.java deleted file mode 100644 index 257112b..0000000 --- a/src/main/java/fr/irit/smac/amas/controller/command/NewDataPointCommand.java +++ /dev/null @@ -1,8 +0,0 @@ -package fr.irit.smac.amas.controller.command; - -import fr.irit.smac.amas.DataPoint; - -import java.util.List; - -public record NewDataPointCommand(List<DataPoint> newDataPoints) implements Command { -} diff --git a/src/main/java/fr/irit/smac/amas/controller/command/SolveCommand.java b/src/main/java/fr/irit/smac/amas/controller/command/SolveCommand.java deleted file mode 100644 index c43da7d..0000000 --- a/src/main/java/fr/irit/smac/amas/controller/command/SolveCommand.java +++ /dev/null @@ -1,4 +0,0 @@ -package fr.irit.smac.amas.controller.command; - -public record SolveCommand() implements Command { -} diff --git a/src/main/java/fr/irit/smac/amas/controller/query/Query.java b/src/main/java/fr/irit/smac/amas/controller/query/Query.java deleted file mode 100644 index 9093d15..0000000 --- a/src/main/java/fr/irit/smac/amas/controller/query/Query.java +++ /dev/null @@ -1,4 +0,0 @@ -package fr.irit.smac.amas.controller.query; - -public interface Query { -} diff --git a/src/main/java/fr/irit/smac/clumate/CluMATE.java b/src/main/java/fr/irit/smac/clumate/CluMATE.java new file mode 100644 index 0000000..d1c1d42 --- /dev/null +++ b/src/main/java/fr/irit/smac/clumate/CluMATE.java @@ -0,0 +1,26 @@ +package fr.irit.smac.clumate; + +import fr.irit.smac.clumate.amas.MASSettings; +import fr.irit.smac.clumate.amas.controller.CluMATEController; +import fr.irit.smac.clumate.amas.controller.command.NewDataPointCommand; +import fr.irit.smac.clumate.amas.controller.command.SolveCommand; +import fr.irit.smac.clumate.amas.controller.query.Result; +import fr.irit.smac.clumate.amas.controller.query.RetrieveClustersResultQuery; +import fr.irit.smac.clumate.cluster.DataPoint; + +import java.util.List; + +public class CluMATE<T extends DataPoint> { + private final CluMATEController<T> controller; + + public CluMATE(MASSettings<T> masSettings) { + this.controller = new CluMATEController<T>(masSettings); + } + public void fit(List<T> data) { + controller.handle(new NewDataPointCommand<T>(data)); + controller.handle(new SolveCommand()); + } + public Result<T> retrieveClusters() { + return controller.handle(new RetrieveClustersResultQuery()); + } +} diff --git a/src/main/java/fr/irit/smac/amas/AMASOption.java b/src/main/java/fr/irit/smac/clumate/amas/AMASOption.java similarity index 50% rename from src/main/java/fr/irit/smac/amas/AMASOption.java rename to src/main/java/fr/irit/smac/clumate/amas/AMASOption.java index ac73164..8d83d0a 100644 --- a/src/main/java/fr/irit/smac/amas/AMASOption.java +++ b/src/main/java/fr/irit/smac/clumate/amas/AMASOption.java @@ -1,4 +1,4 @@ -package fr.irit.smac.amas; +package fr.irit.smac.clumate.amas; public enum AMASOption { Forget diff --git a/src/main/java/fr/irit/smac/amas/ClusterAMAS.java b/src/main/java/fr/irit/smac/clumate/amas/ClusterAMAS.java similarity index 78% rename from src/main/java/fr/irit/smac/amas/ClusterAMAS.java rename to src/main/java/fr/irit/smac/clumate/amas/ClusterAMAS.java index 08a950b..ed241f0 100644 --- a/src/main/java/fr/irit/smac/amas/ClusterAMAS.java +++ b/src/main/java/fr/irit/smac/clumate/amas/ClusterAMAS.java @@ -1,24 +1,22 @@ -package fr.irit.smac.amas; +package fr.irit.smac.clumate.amas; import fr.irit.smac.amak.Amas; +import fr.irit.smac.clumate.cluster.DataPoint; import lombok.Getter; import lombok.Setter; -import java.util.ArrayList; -import java.util.EnumSet; -import java.util.List; import java.util.function.Consumer; import java.util.logging.Level; import java.util.logging.Logger; -public class ClusterAMAS extends Amas<ClusterEnvironment> { +public class ClusterAMAS<T extends DataPoint> extends Amas<ClusterEnvironment<T>> { private static final Logger logger = Logger.getLogger(ClusterAMAS.class.getName()); @Setter private Consumer<Integer> onCycleEnd; @Getter - private final MASSettings masSettings; + private final MASSettings<T> masSettings; - public ClusterAMAS(ClusterEnvironment environment, MASSettings masSettings) { + public ClusterAMAS(ClusterEnvironment<T> environment, MASSettings<T> masSettings) { super(environment, 1, ExecutionPolicy.TWO_PHASES); this.masSettings = masSettings; } @@ -29,7 +27,7 @@ public class ClusterAMAS extends Amas<ClusterEnvironment> { logger.info("---- BEGIN MAS CYCLE ----"); if (agents.isEmpty() || allAgentsAreDormant()) { var newDataPoint = environment.pollPendingDataPoint(); - newDataPoint.ifPresent(dataPoint -> new ClusterAgent(this, dataPoint)); + newDataPoint.ifPresent(dataPoint -> new ClusterAgent<T>(this, dataPoint)); } } diff --git a/src/main/java/fr/irit/smac/amas/ClusterAgent.java b/src/main/java/fr/irit/smac/clumate/amas/ClusterAgent.java similarity index 83% rename from src/main/java/fr/irit/smac/amas/ClusterAgent.java rename to src/main/java/fr/irit/smac/clumate/amas/ClusterAgent.java index 94d7b7f..63eb488 100644 --- a/src/main/java/fr/irit/smac/amas/ClusterAgent.java +++ b/src/main/java/fr/irit/smac/clumate/amas/ClusterAgent.java @@ -1,9 +1,11 @@ -package fr.irit.smac.amas; +package fr.irit.smac.clumate.amas; import fr.irit.smac.amak.Agent; import fr.irit.smac.amak.messaging.Mailbox; -import fr.irit.smac.amas.messages.EvaluatedScoreMessage; -import fr.irit.smac.amas.messages.RequestSimilarityMessage; +import fr.irit.smac.clumate.amas.messages.EvaluatedScoreMessage; +import fr.irit.smac.clumate.amas.messages.RequestSimilarityMessage; +import fr.irit.smac.clumate.cluster.Cluster; +import fr.irit.smac.clumate.cluster.DataPoint; import lombok.Getter; import java.text.MessageFormat; @@ -11,10 +13,10 @@ import java.util.*; import java.util.logging.Level; import java.util.logging.Logger; -public class ClusterAgent extends Agent<ClusterAMAS, ClusterEnvironment> { +public class ClusterAgent<T extends DataPoint> extends Agent<ClusterAMAS<T>, ClusterEnvironment<T>> { private static final float FLOAT_COMPARISON_EPSILON = 0.000001f; @Getter - private Cluster cluster; + private Cluster<T> cluster; private State state = State.INIT; private State nextState = state; @@ -24,9 +26,9 @@ public class ClusterAgent extends Agent<ClusterAMAS, ClusterEnvironment> { //endregion //region Perception variables - private Cluster receivedClusterForRequestSimilarity; - private ClusterAgent receivedRequestSimilarityRequester; - private final Map<ClusterAgent, Float> similarityScoresReceived = new HashMap<>(); + private Cluster<T> receivedClusterForRequestSimilarity; + private ClusterAgent<T> receivedRequestSimilarityRequester; + private final Map<ClusterAgent<T>, Float> similarityScoresReceived = new HashMap<>(); //endregion //region Decision variables @@ -36,17 +38,17 @@ public class ClusterAgent extends Agent<ClusterAMAS, ClusterEnvironment> { //endregion //region Action variables - private final List<ClusterAgent> requestSimilarityMessageSentAgents = new ArrayList<>(); + private final List<ClusterAgent<T>> requestSimilarityMessageSentAgents = new ArrayList<>(); //endregion - private final List<ClusterAgent> clusterAgentsRoughlySimilarOnReady = new ArrayList<>(); + private final List<ClusterAgent<T>> clusterAgentsRoughlySimilarOnReady = new ArrayList<>(); private final Random random = new Random(); private static final Logger logger = Logger.getLogger(ClusterAgent.class.getName()); private int amountOfCyclesWithTheSameState; - protected ClusterAgent(ClusterAMAS amas, DataPoint dataPoint) { + protected ClusterAgent(ClusterAMAS amas, T dataPoint) { super(amas); - this.cluster = new Cluster(dataPoint); + this.cluster = new Cluster<T>(dataPoint); } public enum State {DORMANT, WAITING_FOR_REPLY, DECIDING, INIT, DEAD, ACTIVE} @@ -67,7 +69,7 @@ public class ClusterAgent extends Agent<ClusterAMAS, ClusterEnvironment> { } } - private boolean isRoughlySimilarTo(ClusterAgent otherClusterAgent) { + private boolean isRoughlySimilarTo(ClusterAgent<T> otherClusterAgent) { return getAmas().getMasSettings().similarityScoreMethod().areRoughlySimilar(cluster.getRepresentative(), otherClusterAgent.cluster.getRepresentative()); } @@ -240,15 +242,21 @@ public class ClusterAgent extends Agent<ClusterAMAS, ClusterEnvironment> { state = nextState; } - private void fuse(ClusterAgent other) { + private void fuse(ClusterAgent<T> other) { if (logger.isLoggable(Level.INFO)) logger.info(this + " fuses with " + other); - cluster = cluster.fuse(other.cluster); + + cluster = fuse(other.cluster); + } + + private Cluster<T> fuse(Cluster<T> other) { + T newRepresentative = (T) amas.getMasSettings().dataPointFuser().apply(cluster.getRepresentative(), other.getRepresentative()); + return new Cluster<T>(newRepresentative, cluster.getSize()+other.getSize()); } - private List<ClusterAgent> getRequestedAgentsWithSimilarityScoreAboveFusionThreshold(Map<ClusterAgent, Float> similarityScoresReceived) { + private List<ClusterAgent<T>> getRequestedAgentsWithSimilarityScoreAboveFusionThreshold(Map<ClusterAgent<T>, Float> similarityScoresReceived) { float highestScore = Float.MIN_VALUE; - List<ClusterAgent> agentsWithHighestScore = new ArrayList<>(); + List<ClusterAgent<T>> agentsWithHighestScore = new ArrayList<>(); for (var e : similarityScoresReceived.entrySet()) { if (e.getValue() >= getAmas().getMasSettings().fusionThreshold()) { if (Math.abs(e.getValue() - highestScore) < FLOAT_COMPARISON_EPSILON) { @@ -264,7 +272,7 @@ public class ClusterAgent extends Agent<ClusterAMAS, ClusterEnvironment> { } - private float computeSimilarityScore(ClusterAgent other) { + private float computeSimilarityScore(ClusterAgent<T> other) { return getAmas().getMasSettings().similarityScoreMethod().apply(this.cluster.getRepresentative(), other.cluster.getRepresentative()); } } diff --git a/src/main/java/fr/irit/smac/amas/ClusterEnvironment.java b/src/main/java/fr/irit/smac/clumate/amas/ClusterEnvironment.java similarity index 66% rename from src/main/java/fr/irit/smac/amas/ClusterEnvironment.java rename to src/main/java/fr/irit/smac/clumate/amas/ClusterEnvironment.java index 8b98b81..914f824 100644 --- a/src/main/java/fr/irit/smac/amas/ClusterEnvironment.java +++ b/src/main/java/fr/irit/smac/clumate/amas/ClusterEnvironment.java @@ -1,6 +1,7 @@ -package fr.irit.smac.amas; +package fr.irit.smac.clumate.amas; import fr.irit.smac.amak.Environment; +import fr.irit.smac.clumate.cluster.DataPoint; import lombok.Getter; import java.util.List; @@ -9,12 +10,12 @@ import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.logging.Logger; -public class ClusterEnvironment extends Environment { +public class ClusterEnvironment<T extends DataPoint> extends Environment { - private final Queue<DataPoint> pendingAdditionDataPoints = new ConcurrentLinkedQueue<>(); + private final Queue<T> pendingAdditionDataPoints = new ConcurrentLinkedQueue<>(); private static final Logger logger = Logger.getLogger(ClusterEnvironment.class.getName()); @Getter - private DataPoint lastPolledPendingDataPoint = null; + private T lastPolledPendingDataPoint = null; @Override public void onInitialEntitiesCreation() { @@ -24,13 +25,13 @@ public class ClusterEnvironment extends Environment { public boolean hasRemainingPendingAdditionDataPoints() { return !pendingAdditionDataPoints.isEmpty(); } - public Optional<DataPoint> pollPendingDataPoint() { + public Optional<T> pollPendingDataPoint() { var dataPoint = pendingAdditionDataPoints.poll(); if (dataPoint != null) lastPolledPendingDataPoint = dataPoint; return Optional.ofNullable(dataPoint); } - public void addDataPoints(List<DataPoint> newDataPoints) { + public void addDataPoints(List<T> newDataPoints) { pendingAdditionDataPoints.addAll(newDataPoints); } } diff --git a/src/main/java/fr/irit/smac/clumate/amas/MASSettings.java b/src/main/java/fr/irit/smac/clumate/amas/MASSettings.java new file mode 100644 index 0000000..c8721cf --- /dev/null +++ b/src/main/java/fr/irit/smac/clumate/amas/MASSettings.java @@ -0,0 +1,11 @@ +package fr.irit.smac.clumate.amas; + +import fr.irit.smac.clumate.cluster.DataPoint; +import fr.irit.smac.clumate.cluster.DataPointFuser; +import fr.irit.smac.clumate.cluster.SimilarityScoreMethod; + +import java.util.EnumSet; + +public record MASSettings<T extends DataPoint>(SimilarityScoreMethod<T> similarityScoreMethod, float fusionThreshold, EnumSet<AMASOption> amasOptions, DataPointFuser<T> dataPointFuser) { + +} diff --git a/src/main/java/fr/irit/smac/clumate/amas/controller/CluMATEController.java b/src/main/java/fr/irit/smac/clumate/amas/controller/CluMATEController.java new file mode 100644 index 0000000..64bcb2b --- /dev/null +++ b/src/main/java/fr/irit/smac/clumate/amas/controller/CluMATEController.java @@ -0,0 +1,51 @@ +package fr.irit.smac.clumate.amas.controller; + +import fr.irit.smac.amak.scheduling.Scheduler; +import fr.irit.smac.clumate.amas.controller.command.NewDataPointCommand; +import fr.irit.smac.clumate.amas.controller.command.ShutdownCommand; +import fr.irit.smac.clumate.amas.controller.command.SolveCommand; +import fr.irit.smac.clumate.amas.controller.query.Result; +import fr.irit.smac.clumate.amas.controller.query.RetrieveClustersResultQuery; +import fr.irit.smac.clumate.amas.ClusterAMAS; +import fr.irit.smac.clumate.amas.ClusterAgent; +import fr.irit.smac.clumate.amas.ClusterEnvironment; +import fr.irit.smac.clumate.amas.MASSettings; +import fr.irit.smac.clumate.cluster.DataPoint; +import lombok.Getter; + +import java.util.concurrent.Executors; + +public class CluMATEController<T extends DataPoint> { + @Getter + private final ClusterEnvironment<T> environment; + @Getter + private final MASSettings<T> masSettings; + @Getter + private final ClusterAMAS<T> amas; + @Getter + private final Scheduler scheduler; + + public CluMATEController(MASSettings<T> masSettings) { + this.masSettings = masSettings; + environment = new ClusterEnvironment<T>(); + amas = new ClusterAMAS<T>(environment, masSettings); + scheduler = new Scheduler(Executors.newSingleThreadExecutor(), amas, environment); + } + + public void handle(NewDataPointCommand<T> newDataPointCommand) { + environment.addDataPoints(newDataPointCommand.newDataPoints()); + } + + public void handle(SolveCommand solveCommand) { + scheduler.startWithSleepSync(0); + } + + public void handle(ShutdownCommand shutdownCommand) { + scheduler.stop(); + } + + public Result<T> handle(RetrieveClustersResultQuery retrieveClustersResultQuery) { + var clusters = amas.getAgents(ClusterAgent.class).stream().map(agent -> ((ClusterAgent<T>) agent).getCluster()).toList(); + return new Result<T>(clusters, masSettings); + } +} diff --git a/src/main/java/fr/irit/smac/clumate/amas/controller/command/Command.java b/src/main/java/fr/irit/smac/clumate/amas/controller/command/Command.java new file mode 100644 index 0000000..ed8e0ad --- /dev/null +++ b/src/main/java/fr/irit/smac/clumate/amas/controller/command/Command.java @@ -0,0 +1,4 @@ +package fr.irit.smac.clumate.amas.controller.command; + +public interface Command { +} diff --git a/src/main/java/fr/irit/smac/clumate/amas/controller/command/NewDataPointCommand.java b/src/main/java/fr/irit/smac/clumate/amas/controller/command/NewDataPointCommand.java new file mode 100644 index 0000000..1ec497c --- /dev/null +++ b/src/main/java/fr/irit/smac/clumate/amas/controller/command/NewDataPointCommand.java @@ -0,0 +1,8 @@ +package fr.irit.smac.clumate.amas.controller.command; + +import fr.irit.smac.clumate.cluster.DataPoint; + +import java.util.List; + +public record NewDataPointCommand<T extends DataPoint>(List<T> newDataPoints) implements Command { +} diff --git a/src/main/java/fr/irit/smac/amas/controller/command/ShutdownCommand.java b/src/main/java/fr/irit/smac/clumate/amas/controller/command/ShutdownCommand.java similarity index 50% rename from src/main/java/fr/irit/smac/amas/controller/command/ShutdownCommand.java rename to src/main/java/fr/irit/smac/clumate/amas/controller/command/ShutdownCommand.java index cbb39fa..2345a49 100644 --- a/src/main/java/fr/irit/smac/amas/controller/command/ShutdownCommand.java +++ b/src/main/java/fr/irit/smac/clumate/amas/controller/command/ShutdownCommand.java @@ -1,4 +1,4 @@ -package fr.irit.smac.amas.controller.command; +package fr.irit.smac.clumate.amas.controller.command; public record ShutdownCommand() implements Command { } diff --git a/src/main/java/fr/irit/smac/clumate/amas/controller/command/SolveCommand.java b/src/main/java/fr/irit/smac/clumate/amas/controller/command/SolveCommand.java new file mode 100644 index 0000000..7af6bdf --- /dev/null +++ b/src/main/java/fr/irit/smac/clumate/amas/controller/command/SolveCommand.java @@ -0,0 +1,4 @@ +package fr.irit.smac.clumate.amas.controller.command; + +public record SolveCommand() implements Command { +} diff --git a/src/main/java/fr/irit/smac/clumate/amas/controller/query/Query.java b/src/main/java/fr/irit/smac/clumate/amas/controller/query/Query.java new file mode 100644 index 0000000..c6ae431 --- /dev/null +++ b/src/main/java/fr/irit/smac/clumate/amas/controller/query/Query.java @@ -0,0 +1,4 @@ +package fr.irit.smac.clumate.amas.controller.query; + +public interface Query { +} diff --git a/src/main/java/fr/irit/smac/clumate/amas/controller/query/Result.java b/src/main/java/fr/irit/smac/clumate/amas/controller/query/Result.java new file mode 100644 index 0000000..aa5fba7 --- /dev/null +++ b/src/main/java/fr/irit/smac/clumate/amas/controller/query/Result.java @@ -0,0 +1,20 @@ +package fr.irit.smac.clumate.amas.controller.query; + +import fr.irit.smac.clumate.amas.MASSettings; +import fr.irit.smac.clumate.cluster.Cluster; +import fr.irit.smac.clumate.cluster.DataPoint; + +import java.util.List; + + +public record Result<T extends DataPoint>(List<Cluster<T>> clusters, + int amountOfClustersWithFrequencyEqualToOne, + int amountOfClustersWithFrequencyHigherThan5, + MASSettings<T> masSettings) { + public Result(List<Cluster<T>> clusters, MASSettings<T> masSettings) { + this(clusters, + (int) clusters.stream().filter(l -> l.getSize() == 1).count(), + (int) clusters.stream().filter(l -> l.getSize() > 5).count(), + masSettings); + } +} diff --git a/src/main/java/fr/irit/smac/amas/controller/query/RetrieveClustersResultQuery.java b/src/main/java/fr/irit/smac/clumate/amas/controller/query/RetrieveClustersResultQuery.java similarity index 55% rename from src/main/java/fr/irit/smac/amas/controller/query/RetrieveClustersResultQuery.java rename to src/main/java/fr/irit/smac/clumate/amas/controller/query/RetrieveClustersResultQuery.java index e11e99d..efe797e 100644 --- a/src/main/java/fr/irit/smac/amas/controller/query/RetrieveClustersResultQuery.java +++ b/src/main/java/fr/irit/smac/clumate/amas/controller/query/RetrieveClustersResultQuery.java @@ -1,4 +1,4 @@ -package fr.irit.smac.amas.controller.query; +package fr.irit.smac.clumate.amas.controller.query; public record RetrieveClustersResultQuery() implements Query { } diff --git a/src/main/java/fr/irit/smac/amas/messages/EvaluatedScoreMessage.java b/src/main/java/fr/irit/smac/clumate/amas/messages/EvaluatedScoreMessage.java similarity index 89% rename from src/main/java/fr/irit/smac/amas/messages/EvaluatedScoreMessage.java rename to src/main/java/fr/irit/smac/clumate/amas/messages/EvaluatedScoreMessage.java index 8ba5c25..67a311e 100644 --- a/src/main/java/fr/irit/smac/amas/messages/EvaluatedScoreMessage.java +++ b/src/main/java/fr/irit/smac/clumate/amas/messages/EvaluatedScoreMessage.java @@ -1,4 +1,4 @@ -package fr.irit.smac.amas.messages; +package fr.irit.smac.clumate.amas.messages; import fr.irit.smac.amak.Agent; import fr.irit.smac.amak.messaging.Message; diff --git a/src/main/java/fr/irit/smac/amas/messages/RequestSimilarityMessage.java b/src/main/java/fr/irit/smac/clumate/amas/messages/RequestSimilarityMessage.java similarity index 77% rename from src/main/java/fr/irit/smac/amas/messages/RequestSimilarityMessage.java rename to src/main/java/fr/irit/smac/clumate/amas/messages/RequestSimilarityMessage.java index a5b02c6..4cae77f 100644 --- a/src/main/java/fr/irit/smac/amas/messages/RequestSimilarityMessage.java +++ b/src/main/java/fr/irit/smac/clumate/amas/messages/RequestSimilarityMessage.java @@ -1,6 +1,6 @@ -package fr.irit.smac.amas.messages; +package fr.irit.smac.clumate.amas.messages; -import fr.irit.smac.amas.Cluster; +import fr.irit.smac.clumate.cluster.Cluster; import fr.irit.smac.amak.Agent; import fr.irit.smac.amak.messaging.Message; import lombok.Getter; diff --git a/src/main/java/fr/irit/smac/clumate/cluster/Cluster.java b/src/main/java/fr/irit/smac/clumate/cluster/Cluster.java new file mode 100644 index 0000000..3c3576a --- /dev/null +++ b/src/main/java/fr/irit/smac/clumate/cluster/Cluster.java @@ -0,0 +1,21 @@ +package fr.irit.smac.clumate.cluster; + +import lombok.Getter; +import lombok.ToString; + +@ToString +public class Cluster<T extends DataPoint> { + @Getter + private final T representative; + @Getter + private final int size; + + public Cluster(T representative) { + this(representative, 1); + } + + public Cluster(T representative, int newSize) { + this.representative = representative; + this.size = newSize; + } +} diff --git a/src/main/java/fr/irit/smac/clumate/cluster/DataPoint.java b/src/main/java/fr/irit/smac/clumate/cluster/DataPoint.java new file mode 100644 index 0000000..6189901 --- /dev/null +++ b/src/main/java/fr/irit/smac/clumate/cluster/DataPoint.java @@ -0,0 +1,4 @@ +package fr.irit.smac.clumate.cluster; + +public interface DataPoint { +} diff --git a/src/main/java/fr/irit/smac/clumate/cluster/DataPointFuser.java b/src/main/java/fr/irit/smac/clumate/cluster/DataPointFuser.java new file mode 100644 index 0000000..2a64227 --- /dev/null +++ b/src/main/java/fr/irit/smac/clumate/cluster/DataPointFuser.java @@ -0,0 +1,6 @@ +package fr.irit.smac.clumate.cluster; + +import java.util.function.BiFunction; + +public interface DataPointFuser<T extends DataPoint> extends BiFunction<T, T, T> { +} diff --git a/src/main/java/fr/irit/smac/clumate/cluster/SimilarityScoreMethod.java b/src/main/java/fr/irit/smac/clumate/cluster/SimilarityScoreMethod.java new file mode 100644 index 0000000..59a6410 --- /dev/null +++ b/src/main/java/fr/irit/smac/clumate/cluster/SimilarityScoreMethod.java @@ -0,0 +1,10 @@ +package fr.irit.smac.clumate.cluster; + + +public interface SimilarityScoreMethod<T extends DataPoint> { + float MAX_SIMILARITY_VALUE = 1.0f; + + float apply(T dp1, T dp2); + + boolean areRoughlySimilar(T dp1, T dp2); +} diff --git a/src/test/groovy/SimpleClusteringIT.groovy b/src/test/groovy/SimpleClusteringIT.groovy new file mode 100644 index 0000000..f77a4ba --- /dev/null +++ b/src/test/groovy/SimpleClusteringIT.groovy @@ -0,0 +1,44 @@ +import fr.irit.smac.clumate.CluMATE +import fr.irit.smac.clumate.amas.AMASOption +import fr.irit.smac.clumate.amas.MASSettings +import fr.irit.smac.clumate.cluster.DataPoint +import fr.irit.smac.clumate.cluster.SimilarityScoreMethod +import spock.lang.Specification + +class SimpleClusteringIT extends Specification { + def "Clustering of two similar 1D objects"() { + given: + def clumate = new CluMATE<OneDDataPoint>(new MASSettings(new OneDSimilarityScoreMethod(), 0.5f, EnumSet.noneOf(AMASOption), (OneDDataPoint dp1, OneDDataPoint dp2) -> { + return new OneDDataPoint(dp1.value + (dp2.value - dp1.value) * 0.1f) + })) + + when: + clumate.fit([new OneDDataPoint(1), new OneDDataPoint(2)]) + var results = clumate.retrieveClusters() + + then: + results.clusters().size() == 1 + results.clusters().get(0).getSize() == 2 + } + + class OneDDataPoint implements DataPoint { + public final double value + + OneDDataPoint(double value) { + this.value = value; + } + } + + class OneDSimilarityScoreMethod implements SimilarityScoreMethod<OneDDataPoint> { + + @Override + float apply(OneDDataPoint dp1, OneDDataPoint dp2) { + return Math.abs(dp1.value - dp2.value) <= 1f ? 1f : 0f; + } + + @Override + boolean areRoughlySimilar(OneDDataPoint dp1, OneDDataPoint dp2) { + return true + } + } +} diff --git a/src/test/groovy/SimpleClusteringTest.groovy b/src/test/groovy/SimpleClusteringTest.groovy deleted file mode 100644 index 64b48c7..0000000 --- a/src/test/groovy/SimpleClusteringTest.groovy +++ /dev/null @@ -1,54 +0,0 @@ -import fr.irit.smac.amas.AMASOption -import fr.irit.smac.amas.DataPoint -import fr.irit.smac.amas.MASSettings -import fr.irit.smac.amas.SimilarityScoreMethod -import fr.irit.smac.amas.controller.ClusterAMASController -import fr.irit.smac.amas.controller.command.NewDataPointCommand -import fr.irit.smac.amas.controller.command.SolveCommand -import fr.irit.smac.amas.controller.query.RetrieveClustersResultQuery -import spock.lang.Specification - -class SimpleClusteringTest extends Specification { - def "Clustering of two similar 1D objects"() { - given: - def controller = new ClusterAMASController(new MASSettings(new OneDSimilarityScoreMethod(), 0.5f, EnumSet.noneOf(AMASOption))) - - when: - controller.handle(new NewDataPointCommand([new DataPoint1D(1), new DataPoint1D(2)])) - controller.handle(new SolveCommand()) - var results = controller.handle(new RetrieveClustersResultQuery()) - - then: - results.size() == 1 - results.get(0).getSize() == 2 - } -} - -class DataPoint1D implements DataPoint { - public final double value - - DataPoint1D(double value) { - this.value = value; - } - - @Override - DataPoint fuse(DataPoint representative) { - if (representative instanceof DataPoint1D) { - def other = (DataPoint1D) representative; - return new DataPoint1D(value + (other.value - value) * 0.1f) - } - } -} - -class OneDSimilarityScoreMethod implements SimilarityScoreMethod { - - @Override - float apply(DataPoint dp1, DataPoint dp2) { - return Math.abs(((DataPoint1D) dp1).value - ((DataPoint1D) dp2).value) <= 1f ? 1f : 0f; - } - - @Override - boolean areRoughlySimilar(DataPoint dp1, DataPoint dp2) { - return true - } -} \ No newline at end of file -- GitLab