From f80b9d1074347d8c0eea6313b47fc02319d6a82e Mon Sep 17 00:00:00 2001 From: John Collins Date: Mon, 12 Apr 2021 10:17:59 -0500 Subject: [PATCH] #1076 - Update usage of inertia in tariff evaluation --- .../org/powertac/factoredcustomer/Config.java | 8 +++ .../DefaultCapacityBundle.java | 8 +-- .../DefaultUtilityOptimizer.java | 1 + .../main/resources/config/FrostyStorage.xml | 2 +- .../org/powertac/common/TariffEvaluator.java | 55 ++++++++++++++----- .../powertac/common/TariffEvaluatorTest.java | 26 +++++++-- 6 files changed, 77 insertions(+), 23 deletions(-) diff --git a/factored-customer/src/main/java/org/powertac/factoredcustomer/Config.java b/factored-customer/src/main/java/org/powertac/factoredcustomer/Config.java index 677a04ec8..0d7587135 100644 --- a/factored-customer/src/main/java/org/powertac/factoredcustomer/Config.java +++ b/factored-customer/src/main/java/org/powertac/factoredcustomer/Config.java @@ -145,6 +145,14 @@ public Map> getStructures () return structures; } + /** + * Retrieves structures of the specified type + */ + public Map getSpecifiedStructures (String type) + { + return getStructures().get(type); + } + /** * Singleton accessor */ diff --git a/factored-customer/src/main/java/org/powertac/factoredcustomer/DefaultCapacityBundle.java b/factored-customer/src/main/java/org/powertac/factoredcustomer/DefaultCapacityBundle.java index 844352cac..795b4d99e 100644 --- a/factored-customer/src/main/java/org/powertac/factoredcustomer/DefaultCapacityBundle.java +++ b/factored-customer/src/main/java/org/powertac/factoredcustomer/DefaultCapacityBundle.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2018 the original author or authors. + * Copyright 2011-2021 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. @@ -133,11 +133,11 @@ public void initialize (FactoredCustomerService service, Config config = Config.getInstance(); Map subscribers = - config.getStructures().get("TariffSubscriberStructure"); + config.getSpecifiedStructures("TariffSubscriberStructure"); Map optimizers = - config.getStructures().get("ProfileOptimizerStructure"); + config.getSpecifiedStructures("ProfileOptimizerStructure"); Map capacities = - config.getStructures().get("CapacityStructure"); + config.getSpecifiedStructures("CapacityStructure"); subscriberStructure = (TariffSubscriberStructure) subscribers.get(name); diff --git a/factored-customer/src/main/java/org/powertac/factoredcustomer/DefaultUtilityOptimizer.java b/factored-customer/src/main/java/org/powertac/factoredcustomer/DefaultUtilityOptimizer.java index 51acd72b9..feead7f89 100644 --- a/factored-customer/src/main/java/org/powertac/factoredcustomer/DefaultUtilityOptimizer.java +++ b/factored-customer/src/main/java/org/powertac/factoredcustomer/DefaultUtilityOptimizer.java @@ -210,6 +210,7 @@ public void evaluateTariffs () .getInertiaDistribution().drawSample()); } else { + // WARNING: magic number here log.warn("no inertia distro, using default value 0.7"); evaluator.withInertia(0.7); } diff --git a/factored-customer/src/main/resources/config/FrostyStorage.xml b/factored-customer/src/main/resources/config/FrostyStorage.xml index 10142689a..13a696fe6 100644 --- a/factored-customer/src/main/resources/config/FrostyStorage.xml +++ b/factored-customer/src/main/resources/config/FrostyStorage.xml @@ -15,7 +15,7 @@ 3 3 THERMAL_STORAGE_CONSUMPTION - LARGE + LARGE true false -500 diff --git a/server-interface/src/main/java/org/powertac/common/TariffEvaluator.java b/server-interface/src/main/java/org/powertac/common/TariffEvaluator.java index af5686f46..06eb4cac6 100644 --- a/server-interface/src/main/java/org/powertac/common/TariffEvaluator.java +++ b/server-interface/src/main/java/org/powertac/common/TariffEvaluator.java @@ -70,7 +70,7 @@ public class TariffEvaluator private TariffEvaluationHelper helper; // per-customer parameter settings - private int chunkSize = 1; // max size of allocation chunks + private int chunkSize = 50; // min size of allocation chunks private int maxChunkCount = 200; // max number of chunks private int tariffEvalDepth = 5; // # of tariffs/powerType to eval private double inertia = 0.8; @@ -186,8 +186,8 @@ public void initializeRegulationFactors (double expectedCurtailment, // parameter settings /** - * Sets the target size of allocation chunks. Default is 1. Actual - * chunk size will be at least 0.5% of the population size. + * Sets the target size of allocation chunks. Default is 50. Actual + * chunk size will be at least population / maxChunkCount. */ public TariffEvaluator withChunkSize (int size) { @@ -198,6 +198,18 @@ public TariffEvaluator withChunkSize (int size) return this; } + /** + * Sets the maximum number of allocation chunks for a given population. + */ + public TariffEvaluator withMaxChunkCount (int count) + { + if (count > 0) + maxChunkCount = count; + else + log.error("max chunk count " + count + " < 0"); + return this; + } + /** * Sets the number of tariffs/broker of each applicable PowerType * to consider. Default is 5, which means that only the 5 most recent @@ -346,6 +358,8 @@ public void evaluateTariffs () // adjust inertia for BOG, accounting for the extra // evaluation cycle at ts 0 + // TODO - This should not be computed per evaluator, but rather at the class level. + // See Issue #1078 double actualInertia = Math.max(0.0, (1.0 - Math.pow(2, 1 - evaluationCounter)) * inertia); @@ -457,8 +471,8 @@ private void evaluateAlternativeTariffs (TariffSubscription current, // Compute the final cost number for each tariff HashMap costs = new HashMap(); - double signupCost = 0.0; for (Tariff tariff: tariffs) { + double signupCost; EvalData eval = evaluatedTariffs.get(tariff); double inconvenience = eval.inconvenience; double cost = eval.costEstimate; @@ -542,6 +556,8 @@ private void evaluateAlternativeTariffs (TariffSubscription current, // For large populations, we do it in chunks. chunk = getChunkSize(population); } + double signupCost = currentTariff.getSignupPayment(); + double adjustedInertia = inertia; while (remainingPopulation > 0) { int count = (int)Math.min(remainingPopulation, chunk); remainingPopulation -= count; @@ -549,17 +565,24 @@ private void evaluateAlternativeTariffs (TariffSubscription current, double inertiaSample = accessor.getInertiaSample(); if (!revoked && withdraw0 <= 0.0 && signupCost <= 0.0 && inertiaSample < inertia) { - // skip this one if not processing revoked tariff, - // or if there is no payment possible from withdrawing, - // or if the customer was not induced by a positive signup cost, - // or if the customer is not paying attention. + // skip this chunk if + // the current tariff is not revoked, + // and there is not a positive payment possible from withdrawing, + // and the customer was not induced by a positive signup cost + // and the customer is not paying attention continue; } - else if (signupCost > 0.0 && - inertiaSample < inertia * signupBonusFactor) { - // Use lower inertia in case the current tariff had a signup bonus - continue; + if (signupCost > 0.0) { + // ff there was a positive signup cost, customers are suspicious + adjustedInertia *= signupBonusFactor; + } + if (count > 1) { + // for a population, we apply inertia by subdividing the population + count = (int)Math.round((double)count * (1.0 - adjustedInertia)); } + else if (inertiaSample < adjustedInertia) + continue; + double tariffSample = accessor.getTariffChoiceSample(); // walk down the list until we run out of probability boolean allocated = false; @@ -811,7 +834,7 @@ public double computeInconvenience (Tariff tariff) } // returns the correct chunk size for a given population - private int getChunkSize (int population) + int getChunkSize (int population) { if (population <= chunkSize) return population; @@ -819,6 +842,12 @@ private int getChunkSize (int population) return Math.max(population / maxChunkCount, chunkSize); } + // test support + HashMap getAllocations () + { + return allocations; + } + // Spring component access ------------------------------------------ private TariffRepo getTariffRepo () { diff --git a/server-interface/src/test/java/org/powertac/common/TariffEvaluatorTest.java b/server-interface/src/test/java/org/powertac/common/TariffEvaluatorTest.java index d59976178..ec62b1ee1 100644 --- a/server-interface/src/test/java/org/powertac/common/TariffEvaluatorTest.java +++ b/server-interface/src/test/java/org/powertac/common/TariffEvaluatorTest.java @@ -161,6 +161,19 @@ private void initSubscription (TariffSubscription sub) } // ------------------------- tests ----------------------------- + + /** + * Verify chunk size calculation + */ + @Test + public void chunkSizeDefaults () + { + assertEquals(50, evaluator.getChunkSize(10000)); + assertEquals(50, evaluator.getChunkSize(8000)); + assertEquals(60, evaluator.getChunkSize(12000)); + assertEquals(30, evaluator.getChunkSize(30)); + } + /** * Test for no new tariffs case. */ @@ -178,14 +191,17 @@ public void noTariffTest () evaluator.withChunkSize(5000); // just two chunks evaluator.evaluateTariffs(); + HashMap alloc = evaluator.getAllocations(); + assertEquals(0, alloc.size(), "no allocations"); + } @Test public void testScaleFactor () { - assertEquals((7 * 24.0) / 48.0, evaluator.getScaleFactor(), 1e-6, "default scale factor"); + assertEquals((7 * 24.0) / 48.0, evaluator.getScaleFactor(), 1e-6, "default scale factor 1"); evaluator.setProfileLength(14 * 24); - assertEquals((14 * 24.0) / 48.0, evaluator.getScaleFactor(), 1e-6, "default scale factor"); + assertEquals((14 * 24.0) / 48.0, evaluator.getScaleFactor(), 1e-6, "default scale factor 2"); } @Test @@ -905,9 +921,9 @@ public Object answer(InvocationOnMock invocation) { evaluator.withChunkSize(50); // 200 chunks evaluator.evaluateTariffs(); assertEquals(3, calls.size(), "three tariffs"); - assertEquals(-5000, calls.get(defaultConsumption).intValue(), "-5000 for default"); - assertEquals(2500, calls.get(bobTariff).intValue(), "+2500 for bob"); - assertEquals(2500, calls.get(jimTariff).intValue(), "+2500 for jim"); + assertEquals(-3000, calls.get(defaultConsumption).intValue(), "-5000 for default"); + assertEquals(1500, calls.get(bobTariff).intValue(), "+2500 for bob"); + assertEquals(1500, calls.get(jimTariff).intValue(), "+2500 for jim"); } // Test min contract duration. Two tariffs from Jim have equal signup