/* * JABM - Java Agent-Based Modeling Toolkit * Copyright (C) 2013 Steve Phelps * * This program 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 (at your option) any later version. * * This program 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. */ package net.sourceforge.jabm.examples.elfarolbar; import java.util.Vector; import net.sourceforge.jabm.EventScheduler; import net.sourceforge.jabm.event.RoundFinishedEvent; import net.sourceforge.jabm.event.RoundStartingEvent; import net.sourceforge.jabm.event.SimEvent; import net.sourceforge.jabm.strategy.AbstractStrategy; import net.sourceforge.jabm.util.MathUtil; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Required; /** * A strategy for inductively predicting future attendance at the bar * based on previous attendance values. * * @author Steve Phelps * */ public abstract class AbstractPredictionStrategy extends AbstractStrategy { /** * The bar tender who is keeping track of historical attendance. */ protected BarTender barTender; /** * The history of our previous predictions. */ protected Vector<Double> previousPredictions; /** * The current forecast error for this strategy. */ protected double forecastError = 0.0; /** * The most recent attendance prediction made by this rule. */ protected double currentPrediction; /** * The recency parameter determines how much weight to put * and more recent forecast errors. */ protected double recency = 0.8; static Logger logger = Logger.getLogger(AbstractPredictionStrategy.class); public AbstractPredictionStrategy() { super(); previousPredictions = new Vector<Double>(100); } @Override public void subscribeToEvents(EventScheduler scheduler) { super.subscribeToEvents(); scheduler.addListener(RoundFinishedEvent.class, this); scheduler.addListener(RoundStartingEvent.class, this); } @Override public void eventOccurred(SimEvent event) { super.eventOccurred(event); if (event instanceof RoundFinishedEvent) { onWeekFinished(); } else if (event instanceof RoundStartingEvent) { onWeekStarting(); } } public void onWeekStarting() { makePrediction(); } public void onWeekFinished() { previousPredictions.add(getCurrentPrediction()); updateForecastError(); } public BarTender getBarTender() { return barTender; } @Required public void setBarTender(BarTender barTender) { this.barTender = barTender; } public int getCurrentWeek() { return (int) getScheduler().getSimulationTime().getTicks(); } /** * Recalculate the forecast error based on our our most recent prediction. */ public void updateForecastError() { int t = getCurrentWeek(); double predicted = previousPredictions.get(t); double actual = barTender.getAttendance(t); double difference = MathUtil.squared(predicted - actual); // Use an exponential moving average to put more weight on recent // errors. forecastError = recency * difference + (1 - recency) * forecastError; } /** * Get the current forecast error for this rule. */ public double getForecastError() { return forecastError; } /** * Get the most recent prediction. * * @return The last prediction made regarding attendance. */ public double getCurrentPrediction() { return currentPrediction; } public double getRecency() { return recency; } /** * Set the recency parameter which determines how much weight to put * on more recent forecast errors. * * @param recency The parameter used to calculate EMA of forecast errors. */ public void setRecency(double recency) { this.recency = recency; } public abstract void makePrediction(); }