es.uam.eps.ir.relison.diffusion.propagation.PullPushStrategyRecommenderPropagationMechanism Maven / Gradle / Ivy
/*
* Copyright (C) 2020 Information Retrieval Group at Universidad Autónoma
* de Madrid, http://ir.ii.uam.es
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package es.uam.eps.ir.relison.diffusion.propagation;
import es.uam.eps.ir.relison.diffusion.data.Data;
import es.uam.eps.ir.relison.diffusion.data.PropagatedInformation;
import es.uam.eps.ir.relison.diffusion.simulation.SimulationEdgeTypes;
import es.uam.eps.ir.relison.diffusion.simulation.UserState;
import es.uam.eps.ir.relison.graph.edges.EdgeOrientation;
import java.io.Serializable;
import java.util.*;
import java.util.stream.Stream;
/**
* Propagation mechanism for the so-called rumour spreading propagation mechanism. Following this strategy, each user
* selects a user each iteration: he catches the information from such user, and shares with him the information he has.
* It is a combination of the pull and push propagation mechanisms. A certain amount of time has to pass before a
* neighbor is selected again.
*
* With a given probability, it selects a neighbor using recommended links. Otherwise, it recommends any link.
*
*
* Reference: A. Demers, D. Greene, C. Hauser, W. Irish, J. Larson. Epidemic algorithms for replicated database maintenance. ACM PODC 1987, pp. 1-12 (1987)
*
*
* @author Javier Sanz-Cruzado ([email protected])
* @author Pablo Castells ([email protected])
*
* @param type of the users
* @param type of the information pieces
* @param type of the parameters.
*
*/
public class PullPushStrategyRecommenderPropagationMechanism implements PropagationMechanism
{
/**
* Number of iterations to wait until a profile can be revisited.
*/
private final int waitTime;
/**
* Edge direction of the neighbors to ask for information.
*/
private final EdgeOrientation orientation;
/**
* For each user, the list of users that user will propagate the information to.
*/
private final Map> propagationList;
/**
* The list of users in the last iterations.
*/
private final Map> lastIterations;
/**
* Probability of selecting a recommended link for propagation.
*/
private final double recProb;
/**
* Random number generator.
*/
private final Random rng;
/**
* Constructor.
* @param waitTime number of iterations to wait until a profile can be revisited.
* @param recProb probability of selecting a recommended link for propagation.
*/
public PullPushStrategyRecommenderPropagationMechanism(int waitTime, double recProb)
{
this(waitTime, EdgeOrientation.IN, recProb);
}
/**
* Constructor.
* @param waitTime number of iterations to wait until a profile can be revisited.
* @param orientation selection of the visits available for contact.
* @param recProb probability of selecting a recommended link for propagation.
*/
public PullPushStrategyRecommenderPropagationMechanism(int waitTime, EdgeOrientation orientation, double recProb)
{
this.waitTime = waitTime;
this.orientation = orientation;
this.lastIterations = new HashMap<>();
this.propagationList = new HashMap<>();
this.recProb = recProb;
this.rng = new Random();
}
@Override
public Stream getUsersToPropagate(PropagatedInformation information, UserState originUser, Data data)
{
if(propagationList.containsKey(originUser.getUserId()))
return propagationList.get(originUser.getUserId()).stream();
return Stream.empty();
}
@Override
public void resetSelections(Data data)
{
propagationList.clear();
// For each user in the network:
data.getAllUsers().forEach((u)->
{
// We first obtain the list of neighbors obtained through recommendation (and the rest of them).
List recNeighs = new ArrayList<>();
List nonRecNeighs = new ArrayList<>();
data.getGraph().getNeighbourhood(u, orientation).forEach(v ->
{
if(orientation.equals(EdgeOrientation.IN))
{
if(data.getGraph().getEdgeType(v, u) == SimulationEdgeTypes.RECOMMEND)
{
recNeighs.add(v);
}
else
{
nonRecNeighs.add(v);
}
}
else if(orientation.equals(EdgeOrientation.OUT))
{
if(data.getGraph().getEdgeType(u,v) == SimulationEdgeTypes.RECOMMEND)
{
recNeighs.add(v);
}
else
{
nonRecNeighs.add(v);
}
}
else
{
if(data.getGraph().containsEdge(u, v) && data.getGraph().getEdgeType(u,v) == SimulationEdgeTypes.RECOMMEND)
{
recNeighs.add(v);
}
else if(data.getGraph().containsEdge(v,u) && data.getGraph().getEdgeType(v,u) == SimulationEdgeTypes.RECOMMEND)
{
recNeighs.add(v);
}
else
{
nonRecNeighs.add(v);
}
}
});
// We check whether the last iterations contains a key:
if(!this.lastIterations.containsKey(u))
{
this.lastIterations.put(u, new ArrayList<>());
}
// Then, we obtain the visited users in the last cases:
List alreadyVisited = this.lastIterations.get(u);
U neigh;
recNeighs.removeAll(alreadyVisited);
nonRecNeighs.removeAll(alreadyVisited);
// To ensure that we can choose a neighbor, if there is no eligible neighbors among the
// recommended ones, we choose from the rest.
if(recNeighs.isEmpty())
{
recNeighs.addAll(nonRecNeighs);
}
// The same applies to the rest (in the opposite direction.
if(nonRecNeighs.isEmpty())
{
nonRecNeighs.addAll(recNeighs);
}
double nextDouble = rng.nextDouble();
if(nextDouble < this.recProb)
{
if(!recNeighs.isEmpty())
{
int index = rng.nextInt(recNeighs.size());
neigh = recNeighs.get(index);
}
else
{
neigh = null;
}
}
else
{
if(!nonRecNeighs.isEmpty())
{
int index = rng.nextInt(nonRecNeighs.size());
neigh = nonRecNeighs.get(index);
}
else
{
neigh = null;
}
}
if(neigh != null)
{
if(!propagationList.containsKey(neigh))
propagationList.put(neigh, new ArrayList<>());
if(!propagationList.get(neigh).contains(u))
propagationList.get(neigh).add(u);
if(!propagationList.containsKey(u))
propagationList.put(u, new ArrayList<>());
if(!propagationList.get(u).contains(neigh))
propagationList.get(u).add(neigh);
alreadyVisited.add(neigh);
}
else
{
alreadyVisited.add(null);
}
// Prune the list
if(alreadyVisited.size() > this.waitTime)
{
alreadyVisited.remove(0);
}
});
}
@Override
public boolean dependsOnInformationPiece()
{
return false;
}
}