package internetz;
import java.text.DecimalFormat;
import logger.PjiitOutputter;
import cern.jet.random.ChiSquare;
/**
* Class describing the learning process of a human for simulation purpose.
* Uses sigmoid approximation or alternatively ChiSquare CDF.
*
* Allows for simulating experience decay, and cut-point E.
*
* @author Oskar Jarczyk
* @since 1.0
* @version 1.4
*
*/
public class Experience {
private LearningCurve lc = null;
private SigmoidCurve sc = null;
private double value; // plain experience
private int top; // hipothetical overlearning
private static final double expStub = 0.03; // as it is 0.03
private static final double decayLevel = 0.0005; // 0,05%
private static final double stupidityLevel = 0.03; // 3%
private static final double cutPoint = 1 - calculateCutPoint(6.0);
protected final static ExperienceSanityCheck esc = new ExperienceSanityCheck();
private enum ApproximationMethod {
SIGMOID, CHI_SQUARE
};
public Experience() {
this(0d, 0, false);
}
public Experience(boolean passionStub) {
this(0d, 0, passionStub);
}
public Experience(double value, int top) {
this(value, top, false);
}
public Experience(double value, int top, boolean passionStub) {
if (passionStub) {
int maxx = SimulationParameters.agentSkillsMaximumExperience;
this.value = maxx * expStub;
this.top = maxx;
} else {
this.value = value;
this.top = top;
}
createMathematicalCurves();
say("Creating Experience object with value: " + this.value
+ " and top: " + this.top);
}
private void createMathematicalCurves() {
this.lc = new LearningCurve();
this.sc = new SigmoidCurve();
}
private static double calculateCutPoint(double k) {
return 1d / (1d + Math.pow(Math.E, -k));
}
public double getDelta() {
return getDelta(ApproximationMethod.SIGMOID);
}
public double decay() {
// boolean dries = false;
if (((this.value) / this.top) <= stupidityLevel) {
// don't decay
return 0;
}
double howMuch = this.top * decayLevel;
if (((this.value - howMuch) / this.top) <= stupidityLevel) {
// never make less than 3%
// dries = true;
this.value = stupidityLevel * this.top;
} else {
this.value = this.value - howMuch;
}
return this.value / this.top;
}
public Boolean decayWithDeath() {
boolean dies = false;
double howMuch = this.top * decayLevel;
if (value - howMuch <= 0) {
dies = true;
this.value = 0;
} else {
this.value = value - howMuch;
}
return dies;
}
public double getDelta(ApproximationMethod method) {
switch (method) {
case SIGMOID:
return sc.getDelta((value / top) > 1. ? 1. : (value / top));
case CHI_SQUARE:
return lc.getDelta((value / top) > 1. ? 1. : (value / top));
default:
break;
}
return lc.getDelta((value / top));
}
public double getValue() {
return value;
}
public double getTop() {
return top;
}
public void setValue(double value) {
this.value = value;
}
public void increment(double how_much) {
this.value += how_much;
DecimalFormat df = new DecimalFormat("#.######");
sanity("Experience incremented by: " + df.format(how_much));
}
private void say(String s) {
PjiitOutputter.say(s);
}
private void sanity(String s) {
PjiitOutputter.sanity(s);
}
/**
* Learning Process represented by Sigmoid function
*
* @author Oskar Jarczyk
* @since 1.0
* @version 1.4
*
*/
class SigmoidCurve {
private double limes = 6;
SigmoidCurve() {
say("Object SigmoidCurve created, ref: " + this);
}
protected double getDelta(double k) {
double result = 0;
if (!SimulationParameters.experienceCutPoint) {
double base = 0;
if (k == 1.){
result = 1;
} else
if ((k < 0.5) && (k >= 0.)) {
base = (-limes) + (k * (2 * limes));
result = 1d / (1d + Math.pow(Math.E, -base));
result = result - (Experience.cutPoint * (Math.abs(1-(2*k))));
if (result < 0.) result = 0.; // because of possible precision issues
} else if ((k < 1.001) && (k > 0.5)) {
base = (-limes) + (k * (2 * limes));
result = 1d / (1d + Math.pow(Math.E, -base));
result = result + (Experience.cutPoint * (Math.abs(1-(2*k))));
if (result > 1.) result = 1.; // possible precision issues
} else if (k == 0.5){
base = (-limes) + (k * (2 * limes));
result = 1d / (1d + Math.pow(Math.E, -base));
}
else {
assert false;
// if not, smth would be wrong
}
} else {
throw new UnsupportedOperationException();
// TODO: finish implementation
}
assert result >= 0.;
assert result <= 1.;
return result;
}
}
/**
*
* To jest nasza funkcja delty! delta(E) Ta klasa nie ma nic wspolnego ze
* zmienna E (doswiadczenia) a sluzy jedyni otrzymaniu wartosci delta z E
*
* @author Oskar
* @since 1.1
*/
class LearningCurve {
cern.jet.random.ChiSquare chi = null;
double xLearningAxis = 15; // osi x
int freedom = 6;
LearningCurve() {
say("Object LearningCurve created, with ref: " + this);
chi = new ChiSquare(freedom,
cern.jet.random.ChiSquare.makeDefaultGenerator());
}
private double getDelta(double k) {
double x = chi.cdf(k * xLearningAxis);
DecimalFormat df = new DecimalFormat("#.######");
// NOTE: freedom (x axis of CDF) should be between 0 and 4
say("getDelta for k: " + df.format(k) + " returned x:"
+ df.format(x));
return x;
}
}
}
class ExperienceSanityCheck {
private ChiSquare chi;
private int freedom;
private int k;
// private SigmoidCurve sigmoidCurve;
public static double EpsilonCutValue;
ExperienceSanityCheck() {
freedom = 6; // osi x
k = 15;
chi = new ChiSquare(freedom,
cern.jet.random.ChiSquare.makeDefaultGenerator());
// sigmoidCurve = new SigmoidCurve();
checkChi();
checkSigmoid();
EpsilonCutValue = checkEpsilonFromChi();
}
public void checkChi() {
say("chi.cdf(0.1): " + chi.cdf(0.1 * k));
say("chi.cdf(0.2): " + chi.cdf(0.2 * k));
say("chi.cdf(0.3): " + chi.cdf(0.3 * k));
say("chi.cdf(0.6): " + chi.cdf(0.6 * k));
say("chi.cdf(0.8): " + chi.cdf(0.8 * k));
say("chi.cdf(0.9): " + chi.cdf(0.9 * k));
say("chi.cdf(0.95): " + chi.cdf(0.95 * k));
say("chi.cdf(0.9): " + chi.cdf(0.999 * k));
say("chi.nextDouble(): " + chi.nextDouble());
}
public void checkSigmoid() {
say("sigmoid(-10.000): " + sigmoidGetDelta(-10d));
say("sigmoid(-8.000): " + sigmoidGetDelta(-8d));
say("sigmoid(-6.000): " + sigmoidGetDelta(-6d));
say("sigmoid(-3.000): " + sigmoidGetDelta(-3d));
say("sigmoid(0.000): " + sigmoidGetDelta(0d));
say("sigmoid(0.005): " + sigmoidGetDelta(0.005d));
say("sigmoid(0.505): " + sigmoidGetDelta(0.505d));
say("sigmoid(0.995): " + sigmoidGetDelta(0.995d));
say("sigmoid(1.000): " + sigmoidGetDelta(1d));
say("sigmoid(3.000): " + sigmoidGetDelta(3d));
say("sigmoid(6.000): " + sigmoidGetDelta(6d));
say("sigmoid(8.000): " + sigmoidGetDelta(8d));
say("sigmoid(10.000): " + sigmoidGetDelta(10d));
}
public double checkEpsilonFromChi() {
double e = chi.cdf(1 * k);
say("chi.cdf(1): " + e);
return 1 - e;
}
private double sigmoidGetDelta(double k) {
return 1d / (1d + Math.pow(Math.E, -k));
}
private void say(String s) {
PjiitOutputter.say(s);
}
}