
org.powertac.factoredcustomer.DefaultUtilityOptimizer Maven / Gradle / Ivy
/*
* Copyright 2011-2013 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 org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.joda.time.Instant;
import org.powertac.common.CustomerInfo;
import org.powertac.common.RandomSeed;
import org.powertac.common.Tariff;
import org.powertac.common.TariffEvaluator;
import org.powertac.common.TariffSubscription;
import org.powertac.common.TimeService;
import org.powertac.common.Timeslot;
import org.powertac.common.enumerations.PowerType;
import org.powertac.common.interfaces.CustomerModelAccessor;
import org.powertac.common.interfaces.TariffMarket;
import org.powertac.common.repo.RandomSeedRepo;
import org.powertac.common.repo.TariffRepo;
import org.powertac.common.repo.TariffSubscriptionRepo;
import org.powertac.common.repo.TimeslotRepo;
import org.powertac.common.state.Domain;
import org.powertac.common.state.StateChange;
import org.powertac.factoredcustomer.interfaces.CapacityBundle;
import org.powertac.factoredcustomer.interfaces.CapacityOriginator;
import org.powertac.factoredcustomer.interfaces.UtilityOptimizer;
import org.powertac.factoredcustomer.utils.SeedIdGenerator;
import java.util.HashMap;
import java.util.List;
/**
* Key class responsible for managing the tariff(s) for one customer across
* multiple capacity bundles if necessary.
*
* @author Prashant Reddy, John Collins
*/
@Domain
class DefaultUtilityOptimizer implements UtilityOptimizer
{
private static Logger log =
LogManager.getLogger(DefaultUtilityOptimizer.class.getName());
protected FactoredCustomerService service;
private final CustomerStructure customerStructure;
protected final List capacityBundles;
protected RandomSeed inertiaSampler;
protected RandomSeed tariffSelector;
private HashMap evaluatorMap;
DefaultUtilityOptimizer (CustomerStructure customerStructure,
List bundles)
{
this.customerStructure = customerStructure;
this.capacityBundles = bundles;
this.evaluatorMap = new HashMap<>();
// create evaluation wrappers and tariff evaluators for each bundle
for (CapacityBundle bundle : bundles) {
TariffSubscriberStructure subStructure = bundle.getSubscriberStructure();
TariffEvaluator evaluator =
new TariffEvaluator(new TariffEvaluationWrapper(bundle))
.withChunkSize(Math.max(1, bundle.getPopulation() / 1000))
.withTariffSwitchFactor(subStructure.getTariffSwitchFactor())
.withPreferredContractDuration(subStructure.getExpectedDuration())
.withInconvenienceWeight(subStructure.getInconvenienceWeight())
.withRationality(subStructure.getLogitChoiceRationality())
.withEvaluateAllTariffs(true);
evaluator.initializeCostFactors(subStructure.getExpMeanPriceWeight(),
subStructure.getMaxValuePriceWeight(),
subStructure.getRealizedPriceWeight(),
subStructure.getTariffVolumeThreshold());
evaluator.initializeInconvenienceFactors(subStructure.getTouFactor(),
subStructure.getTieredRateFactor(),
subStructure.getVariablePricingFactor(),
subStructure.getInterruptibilityFactor());
evaluatorMap.put(bundle, evaluator);
}
}
@Override
public void initialize (FactoredCustomerService service)
{
this.service = service;
inertiaSampler =
getRandomSeedRepo()
.getRandomSeed("factoredcustomer.DefaultUtilityOptimizer",
SeedIdGenerator.getId(), "InertiaSampler");
tariffSelector =
getRandomSeedRepo()
.getRandomSeed("factoredcustomer.DefaultUtilityOptimizer",
SeedIdGenerator.getId(), "TariffSelector");
subscribeDefault();
}
// ----- Access components through service to support mocking ------
protected RandomSeedRepo getRandomSeedRepo ()
{
return service.getRandomSeedRepo();
}
protected TariffMarket getTariffMarket ()
{
return service.getTariffMarket();
}
protected TariffSubscriptionRepo getTariffSubscriptionRepo ()
{
return service.getTariffSubscriptionRepo();
}
protected TariffRepo getTariffRepo ()
{
return service.getTariffRepo();
}
protected TimeslotRepo getTimeslotRepo ()
{
return service.getTimeslotRepo();
}
// /////////////// TARIFF SUBSCRIPTION //////////////////////
@StateChange
private void subscribe (Tariff tariff, CapacityBundle bundle,
int customerCount, boolean verbose)
{
getTariffMarket().subscribeToTariff(tariff, bundle.getCustomerInfo(),
customerCount);
if (verbose) {
log.info(bundle.getName() + ": Subscribed " + customerCount
+ " customers to tariff " + tariff.getId() + " successfully");
}
}
/**
* @Override hook
**/
protected void subscribeDefault ()
{
for (CapacityBundle bundle : capacityBundles) {
PowerType powerType = bundle.getPowerType();
if (getTariffMarket().getDefaultTariff(powerType) != null) {
log.info(bundle.getName() + ": Subscribing " + bundle.getPopulation()
+ " customers to default " + powerType + " tariff");
subscribe(getTariffMarket().getDefaultTariff(powerType), bundle,
bundle.getPopulation(), false);
}
else {
log.info(bundle.getName() + ": No default tariff for power type "
+ powerType + "; trying generic type");
PowerType genericType = powerType.getGenericType();
if (getTariffMarket().getDefaultTariff(genericType) == null) {
log.error(bundle.getName()
+ ": No default tariff for generic power type "
+ genericType + " either!");
}
else {
log.info(bundle.getName() + ": Subscribing " + bundle.getPopulation()
+ " customers to default " + genericType + " tariff");
subscribe(getTariffMarket().getDefaultTariff(genericType), bundle,
bundle.getPopulation(), false);
}
}
}
}
// Tariff Evaluation --------------------------------
@Override
public void evaluateTariffs ()
{
for (CapacityBundle bundle : capacityBundles) {
TariffEvaluator evaluator = evaluatorMap.get(bundle);
if (bundle.getSubscriberStructure().getInertiaDistribution() != null) {
evaluator.withInertia(bundle.getSubscriberStructure()
.getInertiaDistribution().drawSample());
}
else {
log.warn("no inertia distro, using default value 0.7");
evaluator.withInertia(0.7);
}
evaluator.evaluateTariffs();
}
}
// /////////////// TIMESLOT ACTIVITY //////////////////////
/**
* used by LearningUtilityOptimizer to execute actions
* that need updated repo (currently shifting computations)
*/
@Override
public void updatedSubscriptionRepo ()
{
}
@Override
public void handleNewTimeslot (Timeslot timeslot)
{
usePower(timeslot);
}
private void usePower (Timeslot timeslot)
{
for (CapacityBundle bundle : capacityBundles) {
List subscriptions =
getTariffSubscriptionRepo().findActiveSubscriptionsForCustomer(bundle
.getCustomerInfo());
double totalCapacity = 0.0;
double totalUsageCharge = 0.0;
for (TariffSubscription subscription : subscriptions) {
double usageSign = bundle.getPowerType().isConsumption() ? +1 : -1;
double currCapacity = usageSign * useCapacity(subscription, bundle);
if (Config.getInstance().isUsageChargesLogging()) {
double charge =
subscription.getTariff().getUsageCharge(currCapacity,
subscription
.getTotalUsage(),
false);
totalUsageCharge += charge;
}
subscription.usePower(currCapacity);
totalCapacity += currCapacity;
}
log.info(bundle.getName() + ": Total " + bundle.getPowerType()
+ " capacity for timeslot " + timeslot.getSerialNumber() + " = "
+ totalCapacity);
logUsageCharges(bundle.getName() + ": Total " + bundle.getPowerType()
+ " usage charge for timeslot "
+ timeslot.getSerialNumber() + " = " + totalUsageCharge);
}
}
private double useCapacity (TariffSubscription subscription,
CapacityBundle bundle)
{
double capacity = 0;
for (CapacityOriginator capacityOriginator : bundle.getCapacityOriginators()) {
capacity += capacityOriginator.useCapacity(subscription);
}
return capacity;
}
private String getCustomerName ()
{
return customerStructure.getName();
}
private void logUsageCharges (String msg)
{
if (Config.getInstance().isUsageChargesLogging()) {
log.info(msg);
}
}
@Override
public String toString ()
{
return this.getClass().getCanonicalName() + ":" + getCustomerName();
}
private class TariffEvaluationWrapper implements CustomerModelAccessor
{
private CapacityBundle bundle;
private TariffSubscriberStructure subStructure;
TariffEvaluationWrapper (CapacityBundle bundle)
{
this.bundle = bundle;
subStructure = bundle.getSubscriberStructure();
}
@Override
public CustomerInfo getCustomerInfo ()
{
return bundle.getCustomerInfo();
}
@Override
public org.powertac.common.CapacityProfile getCapacityProfile (Tariff tariff)
{
double usageSign = bundle.getPowerType().isConsumption() ? +1 : -1;
// New code: shifted prediction - correct for tariff-eval purposes
// ===============================================================
double[] newForecast;
// new code
HashMap originator2usage = new HashMap<>();
for (CapacityOriginator capacityOriginator : bundle.getCapacityOriginators()) {
double[] usageForecast = new double[CapacityProfile.NUM_TIMESLOTS];
// BUG FIX: this function is called from forecastCost() and used
// by TariffEvaluationHelper, which assumes the forcast starts
// at the next timeslot
CapacityProfile forecast = capacityOriginator.getForecastForNextTimeslot();
for (int i = 0; i < CapacityProfile.NUM_TIMESLOTS; ++i) {
double hourlyUsage = usageSign * forecast.getCapacity(i);
usageForecast[i] = hourlyUsage;// don't divide yet / bundle.getPopulation();
}
originator2usage.put(capacityOriginator, usageForecast);
}
// Refactored the following code for LearningUtilityOptimizer - shift profile
//
// create dummy subscription for the above usage vector:
// 1 population member under 'tariff', (consuming the sum
// of the originators' usage)
TariffSubscription dummySubscription =
new DummyTariffSubscription(getCustomerInfo(), tariff);
newForecast = adjustForecastPerTariff(
originator2usage, dummySubscription, bundle);
double[] result = newForecast;
Instant start =
service.getTimeService().getCurrentTime().plus(TimeService.HOUR);
return new org.powertac.common.CapacityProfile(result, start);
}
@Override
public double getBrokerSwitchFactor (boolean isSuperseding)
{
double result = subStructure.getBrokerSwitchFactor();
if (isSuperseding) {
return result * 5.0;
}
return result;
}
@Override
public double getTariffChoiceSample ()
{
return tariffSelector.nextDouble();
}
@Override
public double getInertiaSample ()
{
return inertiaSampler.nextDouble();
}
/**
* Is it correct to sum inconveniences over originators? currently every
* shifting customer has 1 originator so this doesn't matter, but it might
* change in the future.
*/
@Override
public double getShiftingInconvenienceFactor (Tariff tariff)
{
double inconv = 0;
for (CapacityOriginator capacityOriginator : bundle.getCapacityOriginators()) {
inconv += capacityOriginator.getShiftingInconvenienceFactor(tariff);
}
return inconv;
}
}
/**
* HACK, accessed from inner class, overriden from derived class
*
* sum all originators' usage arrays
*
* @param originator2usage
* @param dummySubscription
* @param bundle
* @return
*/
public double[] adjustForecastPerTariff (
HashMap originator2usage,
TariffSubscription dummySubscription,
CapacityBundle bundle)
{
// sum all originators' usage arrays
double[] result = new double[originator2usage.values().iterator().next().length];
for (double[] usage : originator2usage.values()) {
for (int i = 0; i < result.length; ++i) {
result[i] += usage[i] / bundle.getPopulation();
}
}
return result;
}
private class DummyTariffSubscription extends TariffSubscription
{
public DummyTariffSubscription (CustomerInfo customer, Tariff tariff)
{
super(customer, tariff);
}
@Override
public int getCustomersCommitted ()
{
return 1;
}
}
}