package com.spbsu.exp.modelexp;
import com.spbsu.commons.func.Evaluator;
import com.spbsu.commons.func.Factory;
import com.spbsu.commons.random.FastRandom;
import com.spbsu.commons.util.Pair;
import com.spbsu.exp.modelexp.managers.SerpManager;
import com.spbsu.exp.modelexp.setup.SimpleExclusive;
import com.spbsu.exp.modelexp.users.UserFactory;
import gnu.trove.map.TObjectDoubleMap;
import gnu.trove.map.TObjectIntMap;
import gnu.trove.map.hash.TObjectDoubleHashMap;
import gnu.trove.map.hash.TObjectIntHashMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* User: solar
* Date: 02.04.15
* Time: 19:18
*/
public class Model {
private final int initialUsers;
private final int days;
private final double pBirth;
private final FastRandom rng;
private List<Experiment> experiments = new ArrayList<>();
public Model(int initialUsers, int days, double pBirth, FastRandom rng) {
this.initialUsers = initialUsers;
this.days = days;
this.pBirth = pBirth;
this.rng = rng;
}
@SafeVarargs
public final double model(Setup setup, Factory<User> usersFactory, Factory<Experiment[]>... managers) {
final List<User> usersPool = new ArrayList<>(20000000);
for (int i = 0; i < initialUsers; i++) {
usersPool.add(usersFactory.create());
}
double result = 0;
for (int day = 0; day < days; day++) {
final TObjectDoubleMap<Experiment> totalScore = new TObjectDoubleHashMap<>();
// daily manager needs
for(int i = 0; i < managers.length; i++) {
final Factory<Experiment[]> manager = managers[i];
final Experiment[] experiments = manager.create();
for (Experiment experiment : experiments) {
setup.add(experiment);
this.experiments.add(experiment);
}
}
// production change
setup.nextDay();
double dayMeanScore = 0;
int userActivity = 0;
for (int hour = 0; hour < 24; hour++) {
final int newborn = rng.nextPoisson(pBirth / 24 * usersPool.size());
for(int i = 0; i < newborn; i++) {
usersPool.add(usersFactory.create());
}
final RandSet<User> userRandSet = new RandSet<>(usersPool.toArray(new User[usersPool.size()]), new Evaluator<User>() {
@Override
public double value(User user) {
return user.activity() / 24;
}
}, rng);
final int hourActivity = rng.nextPoisson(userRandSet.total());
userActivity += hourActivity;
for(int i = 0; i < hourActivity; i++) {
final User user = userRandSet.next();
final Query query = user.next(day, hour);
final Experiment[] config = setup.assign(user, query);
double score = 0;
for(int j = 0; j < config.length; j++) {
if (config[j].relevant(query))
score += config[j].work(query);
}
result += score;
dayMeanScore += score / 24 / hourActivity;
setup.feedback(user, query, config, score);
user.feedback(score);
for (final Experiment experiment : config) {
totalScore.adjustOrPutValue(experiment, score, score);
}
}
}
System.out.println("Day " + day + " score: " + dayMeanScore + " user activity: " + userActivity);
}
return result;
}
private Experiment[] experiments() {
return experiments.toArray(new Experiment[experiments.size()]);
}
public static void main(String[] args) {
final FastRandom rng = new FastRandom(0);
final Model model = new Model(100000, 365, 0.01, rng);
final Setup setup = new SimpleExclusive(rng);
final double score = model.model(setup, new UserFactory(rng), new SerpManager(rng));
TObjectIntMap<Pair<Stat.Verdict, Stat.Verdict>> results = new TObjectIntHashMap<>();
for (Experiment experiment : model.experiments()) {
final Pair<Stat.Verdict, Stat.Verdict> cell = Pair.create(experiment.realScore() > 0 ? Stat.Verdict.GOOD : Stat.Verdict.BAD, setup.score(experiment).status());
results.adjustOrPutValue(cell, 1, 1);
}
System.out.println("Total user experience: " + score);
for (Stat.Verdict verdict : Stat.Verdict.values())
System.out.print("\t\t" + verdict);
System.out.println();
for (Stat.Verdict v1 : Stat.Verdict.values()) {
System.out.print(v1);
for (Stat.Verdict v2 : Stat.Verdict.values()) {
System.out.print("\t" + results.get(Pair.create(v1, v2)));
}
System.out.println();
}
}
public class RandSet<T> {
private final T[] arr;
private final FastRandom rng;
private final double[] weights;
private final double sum;
public RandSet(T[] arr, Evaluator<T> eval, FastRandom rng) {
this.arr = arr;
this.rng = rng;
this.weights = new double[arr.length];
double sum = 0;
for(int i = 0; i < arr.length; i++) {
sum += eval.value(arr[i]);
weights[i] = sum;
}
this.sum = sum;
}
public T next() {
final double v = rng.nextDouble() * sum;
int index = Arrays.binarySearch(weights, v);
index = index > 0 ? index : -(index + 1);
// System.out.println(index > 0 ? weights[index] - weights[index - 1] : weights[index]);
return arr[index];
}
public double total() {
return sum;
}
}
}