Skip to content
Snippets Groups Projects
Commit 86f00ee0 authored by Alexandre's avatar Alexandre
Browse files

Clean code and structure

parent 8c7e7373
Branches
Tags
Loading
Showing
with 171 additions and 138 deletions
CluMATE
\ No newline at end of file
rootProject.name = 'Mascl' rootProject.name = 'CluMATE'
// Uncomment the two next lines when building amak locally // Uncomment the two next lines when building amak locally
include ':amak' include ':amak'
......
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);
}
}
package fr.irit.smac.amas;
import java.util.EnumSet;
public record MASSettings(SimilarityScoreMethod similarityScoreMethod, float fusionThreshold, EnumSet<AMASOption> amasOptions) {
}
package fr.irit.smac.amas;
public interface SimilarityScoreMethod {
float apply(DataPoint dp1, DataPoint dp2);
boolean areRoughlySimilar(DataPoint dp1, DataPoint dp2);
}
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();
}
}
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 {
}
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());
}
}
package fr.irit.smac.amas; package fr.irit.smac.clumate.amas;
public enum AMASOption { public enum AMASOption {
Forget Forget
......
package fr.irit.smac.amas; package fr.irit.smac.clumate.amas;
import fr.irit.smac.amak.Amas; import fr.irit.smac.amak.Amas;
import fr.irit.smac.clumate.cluster.DataPoint;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; 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()); private static final Logger logger = Logger.getLogger(ClusterAMAS.class.getName());
@Setter @Setter
private Consumer<Integer> onCycleEnd; private Consumer<Integer> onCycleEnd;
@Getter @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); super(environment, 1, ExecutionPolicy.TWO_PHASES);
this.masSettings = masSettings; this.masSettings = masSettings;
} }
...@@ -29,7 +27,7 @@ public class ClusterAMAS extends Amas<ClusterEnvironment> { ...@@ -29,7 +27,7 @@ public class ClusterAMAS extends Amas<ClusterEnvironment> {
logger.info("---- BEGIN MAS CYCLE ----"); logger.info("---- BEGIN MAS CYCLE ----");
if (agents.isEmpty() || allAgentsAreDormant()) { if (agents.isEmpty() || allAgentsAreDormant()) {
var newDataPoint = environment.pollPendingDataPoint(); var newDataPoint = environment.pollPendingDataPoint();
newDataPoint.ifPresent(dataPoint -> new ClusterAgent(this, dataPoint)); newDataPoint.ifPresent(dataPoint -> new ClusterAgent<T>(this, dataPoint));
} }
} }
......
package fr.irit.smac.amas; package fr.irit.smac.clumate.amas;
import fr.irit.smac.amak.Agent; import fr.irit.smac.amak.Agent;
import fr.irit.smac.amak.messaging.Mailbox; import fr.irit.smac.amak.messaging.Mailbox;
import fr.irit.smac.amas.messages.EvaluatedScoreMessage; import fr.irit.smac.clumate.amas.messages.EvaluatedScoreMessage;
import fr.irit.smac.amas.messages.RequestSimilarityMessage; 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 lombok.Getter;
import java.text.MessageFormat; import java.text.MessageFormat;
...@@ -11,10 +13,10 @@ import java.util.*; ...@@ -11,10 +13,10 @@ import java.util.*;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; 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; private static final float FLOAT_COMPARISON_EPSILON = 0.000001f;
@Getter @Getter
private Cluster cluster; private Cluster<T> cluster;
private State state = State.INIT; private State state = State.INIT;
private State nextState = state; private State nextState = state;
...@@ -24,9 +26,9 @@ public class ClusterAgent extends Agent<ClusterAMAS, ClusterEnvironment> { ...@@ -24,9 +26,9 @@ public class ClusterAgent extends Agent<ClusterAMAS, ClusterEnvironment> {
//endregion //endregion
//region Perception variables //region Perception variables
private Cluster receivedClusterForRequestSimilarity; private Cluster<T> receivedClusterForRequestSimilarity;
private ClusterAgent receivedRequestSimilarityRequester; private ClusterAgent<T> receivedRequestSimilarityRequester;
private final Map<ClusterAgent, Float> similarityScoresReceived = new HashMap<>(); private final Map<ClusterAgent<T>, Float> similarityScoresReceived = new HashMap<>();
//endregion //endregion
//region Decision variables //region Decision variables
...@@ -36,17 +38,17 @@ public class ClusterAgent extends Agent<ClusterAMAS, ClusterEnvironment> { ...@@ -36,17 +38,17 @@ public class ClusterAgent extends Agent<ClusterAMAS, ClusterEnvironment> {
//endregion //endregion
//region Action variables //region Action variables
private final List<ClusterAgent> requestSimilarityMessageSentAgents = new ArrayList<>(); private final List<ClusterAgent<T>> requestSimilarityMessageSentAgents = new ArrayList<>();
//endregion //endregion
private final List<ClusterAgent> clusterAgentsRoughlySimilarOnReady = new ArrayList<>(); private final List<ClusterAgent<T>> clusterAgentsRoughlySimilarOnReady = new ArrayList<>();
private final Random random = new Random(); private final Random random = new Random();
private static final Logger logger = Logger.getLogger(ClusterAgent.class.getName()); private static final Logger logger = Logger.getLogger(ClusterAgent.class.getName());
private int amountOfCyclesWithTheSameState; private int amountOfCyclesWithTheSameState;
protected ClusterAgent(ClusterAMAS amas, DataPoint dataPoint) { protected ClusterAgent(ClusterAMAS amas, T dataPoint) {
super(amas); super(amas);
this.cluster = new Cluster(dataPoint); this.cluster = new Cluster<T>(dataPoint);
} }
public enum State {DORMANT, WAITING_FOR_REPLY, DECIDING, INIT, DEAD, ACTIVE} public enum State {DORMANT, WAITING_FOR_REPLY, DECIDING, INIT, DEAD, ACTIVE}
...@@ -67,7 +69,7 @@ public class ClusterAgent extends Agent<ClusterAMAS, ClusterEnvironment> { ...@@ -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()); return getAmas().getMasSettings().similarityScoreMethod().areRoughlySimilar(cluster.getRepresentative(), otherClusterAgent.cluster.getRepresentative());
} }
...@@ -240,15 +242,21 @@ public class ClusterAgent extends Agent<ClusterAMAS, ClusterEnvironment> { ...@@ -240,15 +242,21 @@ public class ClusterAgent extends Agent<ClusterAMAS, ClusterEnvironment> {
state = nextState; state = nextState;
} }
private void fuse(ClusterAgent other) { private void fuse(ClusterAgent<T> other) {
if (logger.isLoggable(Level.INFO)) if (logger.isLoggable(Level.INFO))
logger.info(this + " fuses with " + other); 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; float highestScore = Float.MIN_VALUE;
List<ClusterAgent> agentsWithHighestScore = new ArrayList<>(); List<ClusterAgent<T>> agentsWithHighestScore = new ArrayList<>();
for (var e : similarityScoresReceived.entrySet()) { for (var e : similarityScoresReceived.entrySet()) {
if (e.getValue() >= getAmas().getMasSettings().fusionThreshold()) { if (e.getValue() >= getAmas().getMasSettings().fusionThreshold()) {
if (Math.abs(e.getValue() - highestScore) < FLOAT_COMPARISON_EPSILON) { if (Math.abs(e.getValue() - highestScore) < FLOAT_COMPARISON_EPSILON) {
...@@ -264,7 +272,7 @@ public class ClusterAgent extends Agent<ClusterAMAS, ClusterEnvironment> { ...@@ -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()); return getAmas().getMasSettings().similarityScoreMethod().apply(this.cluster.getRepresentative(), other.cluster.getRepresentative());
} }
} }
package fr.irit.smac.amas; package fr.irit.smac.clumate.amas;
import fr.irit.smac.amak.Environment; import fr.irit.smac.amak.Environment;
import fr.irit.smac.clumate.cluster.DataPoint;
import lombok.Getter; import lombok.Getter;
import java.util.List; import java.util.List;
...@@ -9,12 +10,12 @@ import java.util.Queue; ...@@ -9,12 +10,12 @@ import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.logging.Logger; 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()); private static final Logger logger = Logger.getLogger(ClusterEnvironment.class.getName());
@Getter @Getter
private DataPoint lastPolledPendingDataPoint = null; private T lastPolledPendingDataPoint = null;
@Override @Override
public void onInitialEntitiesCreation() { public void onInitialEntitiesCreation() {
...@@ -24,13 +25,13 @@ public class ClusterEnvironment extends Environment { ...@@ -24,13 +25,13 @@ public class ClusterEnvironment extends Environment {
public boolean hasRemainingPendingAdditionDataPoints() { public boolean hasRemainingPendingAdditionDataPoints() {
return !pendingAdditionDataPoints.isEmpty(); return !pendingAdditionDataPoints.isEmpty();
} }
public Optional<DataPoint> pollPendingDataPoint() { public Optional<T> pollPendingDataPoint() {
var dataPoint = pendingAdditionDataPoints.poll(); var dataPoint = pendingAdditionDataPoints.poll();
if (dataPoint != null) if (dataPoint != null)
lastPolledPendingDataPoint = dataPoint; lastPolledPendingDataPoint = dataPoint;
return Optional.ofNullable(dataPoint); return Optional.ofNullable(dataPoint);
} }
public void addDataPoints(List<DataPoint> newDataPoints) { public void addDataPoints(List<T> newDataPoints) {
pendingAdditionDataPoints.addAll(newDataPoints); pendingAdditionDataPoints.addAll(newDataPoints);
} }
} }
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) {
}
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);
}
}
package fr.irit.smac.amas.controller.command; package fr.irit.smac.clumate.amas.controller.command;
public interface Command { public interface Command {
} }
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 {
}
package fr.irit.smac.amas.controller.command; package fr.irit.smac.clumate.amas.controller.command;
public record ShutdownCommand() implements Command { public record ShutdownCommand() implements Command {
} }
package fr.irit.smac.amas.controller.command; package fr.irit.smac.clumate.amas.controller.command;
public record SolveCommand() implements Command { public record SolveCommand() implements Command {
} }
package fr.irit.smac.amas.controller.query; package fr.irit.smac.clumate.amas.controller.query;
public interface Query { public interface Query {
} }
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);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment