All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.powertac.factoredcustomer.TimeseriesGenerator Maven / Gradle / Ivy

/*
* Copyright 2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an
* "AS IS" BASIS,  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/

package org.powertac.factoredcustomer;

import java.io.*;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import java.util.Properties;
import java.util.Random;
import org.joda.time.DateTime;
import org.powertac.common.Timeslot;
import org.powertac.common.repo.RandomSeedRepo;
import org.powertac.common.spring.SpringApplicationContext;
import org.apache.commons.io.IOUtils;

/**
 * Utility class that generates various time series patterns that can be 
 * used as base capacity series by implementations of @code{CapacityOriginator}.
 * 
 * @author Prashant Reddy
 */
final class TimeseriesGenerator
{
    enum ModelType { ARIMA_101x101 }
    enum DataSource { BUILTIN, CLASSPATH, FILEPATH }

    private RandomSeedRepo randomSeedRepo;

    private final Properties modelParams = new Properties();
    
    private final List refSeries = new ArrayList();
    private final Map genSeries = new HashMap();
    
    private final TimeseriesStructure tsStructure;
    
    private final int FORECAST_HORIZON = 2 * 24; // two days
    
    private double Y0;
    private double[] Yd;
    private double[] Yh;
    private double phi1;
    private double Phi1;
    private double theta1;
    private double Theta1;
    private double sigma;
    private double lambda;
    private double gamma;
    
    private Random arimaNoise;
    
    
    TimeseriesGenerator(TimeseriesStructure structure) 
    {
        tsStructure = structure;
        
        switch (tsStructure.modelType) {
        case ARIMA_101x101:
            initArima101x101();
            break;
        default: throw new Error("Unexpected timeseries model type: " + tsStructure.modelType);
        }
    }
    
    private void initArima101x101() 
    {   
        initArima101x101ModelParams();
        initArima101x101RefSeries();
    }
    
    private void initArima101x101ModelParams()
    {
        InputStream paramsStream;
        String paramsName = tsStructure.modelParamsName;
        switch (tsStructure.modelParamsSource) {
        case BUILTIN:
            throw new Error("Unknown builtin model parameters with name: " + paramsName);
            // break;
        case CLASSPATH:
            paramsStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(paramsName);
            break;
        case FILEPATH:
            try {
                paramsStream = new FileInputStream(paramsName);  
            } catch (FileNotFoundException e) {
                throw new Error("Could not find file to initialize model parameters: " + paramsName);
            }
            break;
        default: 
            throw new Error("Unexpected reference timeseries source type: " + tsStructure.refSeriesSource);
         }
        if (paramsStream == null) throw new Error("Model parameters input stream is uninitialized!");

        try {
            modelParams.load(paramsStream);        
        } catch (java.io.IOException e) {
            throw new Error("Error reading model parameters from file: " + paramsName + "; caught IOException: " + e.toString());        
        }
        
        Y0 = Double.parseDouble((String) modelParams.get("Y0"));
        Yd = ParserFunctions.parseDoubleArray((String) modelParams.get("Yd"));
        Yh = ParserFunctions.parseDoubleArray((String) modelParams.get("Yh"));        
        phi1 = Double.parseDouble((String) modelParams.get("phi1"));
        Phi1 = Double.parseDouble((String) modelParams.get("Phi1"));
        theta1 = Double.parseDouble((String) modelParams.get("theta1"));
        Theta1 = Double.parseDouble((String) modelParams.get("Theta1"));
        lambda = Double.parseDouble((String) modelParams.get("lambda"));
        gamma = Double.parseDouble((String) modelParams.get("gamma"));
        sigma = Double.parseDouble((String) modelParams.get("sigma"));
        
        randomSeedRepo = (RandomSeedRepo) SpringApplicationContext.getBean("randomSeedRepo");

        arimaNoise = new Random(randomSeedRepo.getRandomSeed("factoredcustomer.TimeseriesGenerator", 
                                                             tsStructure.hashCode(), "ArimaNoise").getValue());
    }
    
    private void initArima101x101RefSeries()
    {
        InputStream refStream;
        String seriesName = tsStructure.refSeriesName;
        switch (tsStructure.refSeriesSource) {
        case BUILTIN:
            throw new Error("Unknown builtin series name: " + seriesName);
            // break;
        case CLASSPATH:
            refStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(seriesName);
            break;
        case FILEPATH:
            try {
                refStream = new FileInputStream(seriesName);  
            } catch (FileNotFoundException e) {
                throw new Error("Could not find file to initialize reference timeseries: " + seriesName);
            }
            break;
        default: 
            throw new Error("Unexpected reference timeseries source type: " + tsStructure.refSeriesSource);
         }
        if (refStream == null) throw new Error("Reference timeseries input stream is uninitialized!");
            
        try {
            @SuppressWarnings("unchecked")
            List series = (List) IOUtils.readLines(refStream);
            for (String line: series) {
                Double element = Double.parseDouble(line);
                refSeries.add(element);
            }
        } catch (java.io.EOFException e) {
            final int MIN_TIMESERIES_LENGTH = 26;
            if (refSeries.size() < MIN_TIMESERIES_LENGTH) {
                throw new Error("Insufficient data in reference series; expected " + MIN_TIMESERIES_LENGTH 
                                + " elements, found only " +  genSeries.size());
            }
        } catch (java.io.IOException e) {
            throw new Error("Error reading timeseries data from file: " + seriesName + "; caught IOException: " + e.toString());        
        }
    }

    public double generateNext(Timeslot timeslot)
    {
        Double next;
        switch (tsStructure.modelType) {
        case ARIMA_101x101:
            if (genSeries.isEmpty()) {
                initArima101x101GenSeries(timeslot);
            }
            next = genSeries.get(timeslot.getSerialNumber());
            if (next == null) {
                next = generateNextArima101x101(timeslot);
                genSeries.put(timeslot.getSerialNumber(), next); 
            }
            break;
        default: throw new Error("Unexpected timeseries model type: " + tsStructure.modelType);
        }
        return next;
    }
    
    private void initArima101x101GenSeries(Timeslot timeslot)
    {
        int start = timeslot.getSerialNumber();
        for (int i=0; i < refSeries.size(); ++i) {
            genSeries.put(start + i, refSeries.get(i));
        }
    }
    
    private double generateNextArima101x101(Timeslot timeslot)
    {
        /** R code
        boostTimeSeries = function(Xt, lambda, t, N, Xht, Xdt, gamma) {
            return (Xt + (lambda * ((log(t-26))^2/(log(N-26))^2) * ((1 - gamma) * Xht + gamma * Xdt)))
        }
        for (t in compRange) {                  
            Zf[t] = Y0 + Yd[D[t]] + Yh[H[t]] + phi1 * Zf[t-1] + Phi1 * Zf[t-24] #+ rnorm(1, 0, sigma^2) + 
                          theta1 * (Zf[t-1] - Zf[t-2]) + Theta1 * (Zf[t-24] - Zf[t-25]) + 
                          theta1 * Theta1 * (Zf[t-25] - Zf[t-26]) 
            Zbf[t] = boostTimeSeries(Zf[t], lambda, t, N, Yh[H[t]], Yd[D[t]], gamma) #+ rnorm(1, 0, sigma^2)
        }
        **/
      
        DateTime now = timeslot.getStartInstant().toDateTime();
        int day = now.getDayOfWeek();  // 1=Monday, 7=Sunday
        int hour = now.getHourOfDay();  // 0-23
        
        int t = timeslot.getSerialNumber();
        
        double logNext = Y0 + Yd[day-1] + Yh[hour] + phi1 * getLog(t-1) + Phi1 * getLog(t-24) 
                         + theta1 * (getLog(t-1) - getLog(t-2)) + Theta1 * (getLog(t-24) - getLog(t-25)) 
                         + theta1 * Theta1 * (getLog(t-25) - getLog(t-26));
        logNext = logNext + (lambda * (Math.pow(Math.log(t-26), 2) / Math.pow(Math.log(FORECAST_HORIZON - 26), 2)) 
                                       * ((1 - gamma) * Yh[hour] + gamma * Yd[day-1]));
        logNext = logNext + Math.pow(sigma, 2) * arimaNoise.nextGaussian();
        double next = Math.exp(logNext);
        if (Double.isNaN(next)) throw new Error("Generated NaN as next time series element!");
        return next;
    }
    
    private double getLog(int timeslot)
    {
        return Math.log(genSeries.get(timeslot));
    }

} // end class




© 2015 - 2025 Weber Informatics LLC | Privacy Policy