/*
* Copyright (c) 2005-2010 KOM – Multimedia Communications Lab
*
* This file is part of PeerfactSim.KOM.
*
* PeerfactSim.KOM 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
* any later version.
*
* PeerfactSim.KOM 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 PeerfactSim.KOM. If not, see .
*
*/
package de.tud.kom.p2psim.impl.topology.movement.modularosm.transition;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.Set;
import java.util.Vector;
import de.tud.kom.p2psim.api.common.SimHost;
import de.tud.kom.p2psim.api.common.SimHostComponent;
import de.tud.kom.p2psim.api.scenario.ConfigurationException;
import de.tud.kom.p2psim.api.topology.movement.MovementSupported;
import de.tud.kom.p2psim.api.topology.social.SocialView;
import de.tud.kom.p2psim.impl.simengine.Simulator;
import de.tud.kom.p2psim.impl.topology.PositionVector;
import de.tud.kom.p2psim.impl.topology.Topology;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction.AttractionPoint;
import de.tudarmstadt.maki.simonstrator.api.Event;
import de.tudarmstadt.maki.simonstrator.api.EventHandler;
import de.tudarmstadt.maki.simonstrator.api.Randoms;
/**
* This is a {@link TransitionStrategy} for the Social Case. It will be try to
* build groups based on the {@link SocialView} information. For this, it tries
* to assignment the given {@link MovementSupported} objects to the given
* {@link AttractionPoint}s. For the {@link SocialView}, it is required a
* {@link #socialId}, to find the right {@link SocialView}.
*
*
*
* The Strategy has the parameter of {@link #socialFactor},
* {@link #minPauseTime} and {@link #maxPauseTime}. The socialFactor should be a
* value between 0 and 1. It gives the probability for a social based transition
* or the transition to a random {@link AttractionPoint}. If the social based
* transition is selected, then will be used a scoring to find the right
* {@link AttractionPoint}. For that, it will be used only the AttractionPoints,
* of the hosts, which are in the same SocialCluster or are SocialNeighbors. For
* this AttractionPoints it will be find the highest scoring, which is to found
* in {@link #score(MovementSupported, AttractionPoint, List, List, List, List)}
* .
*
*
*
* After the finding of the next {@link AttractionPoint}, it will be scheduled
* an Event, with a delay between min- and maxPauseTime. After this delay, it
* will be tried to assign a new {@link AttractionPoint} like the described
* above.
*
*
* @author Christoph Muenker
* @version 1.0, 02.07.2013
*/
public class SocialTransitionStrategy implements ITransitionStrategy,
EventHandler {
private String socialId = null;
private SocialView socialView;
private List comps = new Vector();
private List aPoints = new Vector();
private Map assignments = new HashMap();
private Map> favoritePlaces = new HashMap>();
private Map mapMsHost = new HashMap();
private Map mapHostMs = new HashMap();
private double minPauseTime = Simulator.MINUTE_UNIT * 0.5;
private double maxPauseTime = Simulator.MINUTE_UNIT * 100;
private double socialFactor = 0.8;
private long numberOfFavoritePlaces = 4;
private Random rand;
private PositionVector worldDimension;
private boolean init = false;
public SocialTransitionStrategy() {
this.rand = Randoms.getRandom(SocialTransitionStrategy.class);
}
private void init() {
if (!init) {
if (socialId == null) {
throw new ConfigurationException(
"SocialId is not set, to find the needed SocialView!");
}
socialView = Topology.getTopology().getSocialView(socialId);
if (socialView == null) {
throw new ConfigurationException(
"Cannot find the right socialView. Is the socialId correct?");
}
if (minPauseTime > maxPauseTime) {
throw new ConfigurationException(
"MinPauseTime should be smaller then maxPauseTime.");
}
worldDimension = Topology.getWorldDimension();
init = true;
}
}
public void setSocialId(String socialId) {
this.socialId = socialId;
}
@Override
public Map getAssignments() {
// FIXME why new? return new HashMap(assignments);
return assignments;
}
@Override
public void setAttractionPoints(List attractionPoints) {
init();
aPoints.addAll(attractionPoints);
}
@Override
public void addComponent(MovementSupported ms) {
comps.add(ms);
mappingHost(ms);
// assign the ms to an attractionPoint, which is near to the ms
// position.
// TODO: needed? We do Transition as next, and this will delete the
// assignment..
AttractionPoint nearest = aPoints.iterator().next();
for (AttractionPoint aPoint : aPoints) {
if (nearest.getRealPosition().getDistance(ms.getRealPosition()) > aPoint
.getRealPosition().getDistance(ms.getRealPosition())) {
nearest = aPoint;
}
}
assignments.put(ms, nearest);
assignFavoritePlaces(ms);
doTransition(ms);
}
public void doTransition(MovementSupported ms) {
List apFavorites = getFavoritePlaces(ms);
List apFriends = getFriendsPlaces(ms);
List apClusters = getClusterPlaces(ms);
List apRandom = getRandomPlaces(ms,
(int) Math.max(aPoints.size() * 0.2, 5));
AttractionPoint ap = null;
if (rand.nextDouble() < socialFactor) {
ap = findHighestScore(ms, apFavorites, apFriends, apClusters,
apRandom);
} else {
List aps = new ArrayList();
aps.addAll(apRandom);
aps.addAll(apFavorites);
ap = aps.get(rand.nextInt(apRandom.size()));
}
assignments.put(ms, ap);
Event.scheduleWithDelay(getPauseTime(), this, ms, 0);
}
private AttractionPoint findHighestScore(MovementSupported ms,
List apFavorites, List apFriends,
List apClusters, List apRandom) {
Set aps = new HashSet();
aps.addAll(apFavorites);
aps.addAll(apFriends);
aps.addAll(apClusters);
aps.addAll(apRandom);
double maxScore = 0;
AttractionPoint maxAp = null;
for (AttractionPoint ap : aps) {
double score = score(ms, ap, apFavorites, apFriends, apClusters,
apRandom);
// System.out.println(score);
if (score > maxScore) {
maxScore = score;
maxAp = ap;
}
}
return maxAp;
}
/**
* Score the given AttractionPoint for the given {@link MovementSupported}.
* (clusterScore/#NodesInAp + friendsScore + 1/#NodesInAp) * socialFactor +
* (distanceScore + penalty) + (1-socialFactor)
*
* clusterScore = 1 if one is in the same cluster in this AP
* friendsScore = 1 if one friend is in the same AP
* penalty = -1 if AP the actually AP is
* distance = 1 - (distance / maxDistance)
*
* @param ms
* @param ap
* @param apFavorites
* @param apFriends
* @param apClusters
* @param apRandom
* @return
*/
private double score(MovementSupported ms, AttractionPoint ap,
List apFavorites, List apFriends,
List apClusters, List apRandom) {
double distance = ms.getRealPosition()
.getDistance(ap.getRealPosition());
double distanceScore = 1 - (distance / worldDimension.getLength());
double clusterScore = 0;
double friendsScore = 0;
if (apClusters.contains(ap)) {
if (occurence(ap, apClusters) > 3) {
// occurence give the number of other peers in this AP
clusterScore = 1.0 / (occurence(ap, apClusters) - 1);
} else {
clusterScore = 1.0;
}
}
if (apFriends.contains(ap)) {
if (occurence(ap, apFriends) > 3) {
// occurence give the number of other peers in this AP
friendsScore = 1.0 / (occurence(ap, apFriends) - 1);
} else {
friendsScore = 1.0;
}
}
// penalty for distance
double penalty = 0;
if (ap.equals(assignments.get(ms))) {
penalty = -1;
}
return (clusterScore / assignedToAp(ap) + friendsScore + 1.0 / assignedToAp(ap))
* socialFactor
+ (distanceScore + penalty) * (1 - socialFactor);
}
// counts the number of the AttractionPoint in the list
private int occurence(AttractionPoint ap, List aps) {
int i = 0;
for (AttractionPoint a : aps) {
if (a.equals(ap)) {
i++;
}
}
return i;
}
private int assignedToAp(AttractionPoint ap) {
int i = 1;
for (Entry entry : assignments
.entrySet()) {
if (entry.getValue().equals(ap)) {
i++;
}
}
return i;
}
private List getRandomPlaces(MovementSupported ms,
int number) {
Collections.shuffle(aPoints);
List result = new Vector();
Iterator iAP = aPoints.iterator();
for (int i = 0; i < number && iAP.hasNext(); i++) {
result.add(iAP.next());
}
return result;
}
private List getClusterPlaces(MovementSupported ms) {
List result = new Vector();
SimHost msHost = mapMsHost.get(ms);
for (SimHost host : socialView.getCluster(msHost)) {
MovementSupported temp = mapHostMs.get(host);
if (assignments.get(temp) != null) {
result.add(assignments.get(temp));
}
}
return result;
}
private List getFriendsPlaces(MovementSupported ms) {
List result = new Vector();
SimHost msHost = mapMsHost.get(ms);
for (SimHost host : socialView.getNeighbors(msHost)) {
MovementSupported temp = mapHostMs.get(host);
if (assignments.get(temp) != null) {
result.add(assignments.get(temp));
}
}
return result;
}
private List getFavoritePlaces(MovementSupported ms) {
return new Vector(favoritePlaces.get(ms));
}
private void assignFavoritePlaces(MovementSupported ms) {
Set msFavoritePlaces = new HashSet();
LinkedList temp = new LinkedList(
aPoints);
Collections.shuffle(temp, rand);
for (int i = 0; i < numberOfFavoritePlaces; i++) {
if (!temp.isEmpty()) {
msFavoritePlaces.add(temp.removeFirst());
}
}
favoritePlaces.put(ms, msFavoritePlaces);
}
private void mappingHost(MovementSupported ms) {
SimHostComponent comp = (SimHostComponent) ms;
SimHost host = comp.getHost();
assert host != null;
mapHostMs.put(host, ms);
mapMsHost.put(ms, host);
}
protected long getPauseTime() {
return (long) ((rand.nextDouble() * (maxPauseTime - minPauseTime)) + minPauseTime);
}
public void setMinPauseTime(long minPauseTime) {
if (minPauseTime < 0) {
throw new ConfigurationException(
"MinPauseTime should be bigger then 0!");
}
this.minPauseTime = minPauseTime;
}
public void setMaxPauseTime(long maxPauseTime) {
if (maxPauseTime < 0) {
throw new ConfigurationException(
"MaxPauseTime should be bigger then 0!");
}
this.maxPauseTime = maxPauseTime;
}
@Override
public void eventOccurred(Object se, int type) {
doTransition((MovementSupported) se);
}
public void setSocialFactor(double socialFactor) {
if (socialFactor < 0 || socialFactor > 1) {
throw new ConfigurationException(
"socialFactor should be between 0 and 1!");
}
this.socialFactor = socialFactor;
}
}