Follow me

Exercise 4: A better energy management

In this exercise, you will try to manage the energy consumption of your system.

Question 1 – Extending the configuration service: Extend the configuration service of your Follow Me application so that it is possible to configure a maximum power per room :
FollowMeConfigurationService

  1. package org.example.follow.me.configuration;
  2. /**
  3. * The FollowMeConfiguration service allows to configure the Follow Me
  4. * application.
  5. */
  6. public interface FollowMeConfiguration {
  7. public int getMaximumNumberOfLightsToTurnOn();
  8. public void setMaximumNumberOfLightsToTurnOn(int maximumNumberOfLightsToTurnOn);
  9. /**
  10. * Gets the maximum allowed energy consumption in Watts in each room
  11. *
  12. * @return the maximum allowed energy consumption in Watts/hours
  13. */
  14. public double getMaximumAllowedEnergyInRoom();
  15. /**
  16. * Sets the maximum allowed energy consumption in Watts in each room
  17. *
  18. * @param maximumEnergy
  19. * the maximum allowed energy consumption in Watts/hours in each room
  20. */
  21. public void setMaximumAllowedEnergyInRoom(double maximumEnergy);
  22. }
package org.example.follow.me.configuration;
 
/**
 * The FollowMeConfiguration service allows to configure the Follow Me
 * application.
 */
public interface FollowMeConfiguration {
    public int getMaximumNumberOfLightsToTurnOn();
    public void setMaximumNumberOfLightsToTurnOn(int maximumNumberOfLightsToTurnOn);
 
    /**
     * Gets the maximum allowed energy consumption in Watts in each room
     * 
     * @return the maximum allowed energy consumption in Watts/hours
     */
    public double getMaximumAllowedEnergyInRoom();
 
    /**
     * Sets the maximum allowed energy consumption in Watts in each room
     * 
     * @param maximumEnergy
     *            the maximum allowed energy consumption in Watts/hours in each room
     */
    public void setMaximumAllowedEnergyInRoom(double maximumEnergy);
}

Implement this service so that the energy consumption of a room does not exceed the given maximum. To this end, you should create a new member variable:

  1. /**
  2. * The maximum energy consumption allowed in a room in Watt:
  3. **/
  4. private double maximumEnergyConsumptionAllowedInARoom = 100.0d;
/**
* The maximum energy consumption allowed in a room in Watt:
**/
private double maximumEnergyConsumptionAllowedInARoom = 100.0d;

Please note that the maximumNumberOfLights (we have introduced earlier) takes precedence over the given maximum power consumption.

To simplify the implementation you can assume that each light as a default 100Watt consumption and that the lights are binary lights only. In such case the number representing light is given by a simple Euclidean division (N = Target/100)

Question 2 – Test: Create a script to test your application and checks it is working according to the specification (the environment should be composed by binary lights only).

Question 3 – Manager: Extend the FollowMeAdministration interface and your manager implementation to add an energy saving goal :

FollowMeAdministration

  1. public interface FollowMeAdministration {
  2. public void setIlluminancePreference(IlluminanceGoal illuminanceGoal);
  3. public illuminanceGoal getIlluminancePreference();
  4. /**
  5. * Configure the energy saving goal.
  6. * @param energyGoal : the targeted energy goal.
  7. */
  8. public void setEnergySavingGoal(EnergyGoal energyGoal);
  9. /**
  10. * Gets the current energy goal.
  11. *
  12. * @return the current energy goal.
  13. */
  14. public EnergyGoal getEnergyGoal();
  15. }
public interface FollowMeAdministration {
 
    public void setIlluminancePreference(IlluminanceGoal illuminanceGoal);
    public illuminanceGoal getIlluminancePreference();
 
    /**
     * Configure the energy saving goal.
     * @param energyGoal : the targeted energy goal.
     */
    public void setEnergySavingGoal(EnergyGoal energyGoal);
 
    /**
     * Gets the current energy goal.
     * 
     * @return the current energy goal.
     */
    public EnergyGoal getEnergyGoal();
 
}

The different levels of energy could be :

  1. package org.example.follow.me.manager;
  2. /**
  3. * This enum describes the different energy goals associated with the
  4. * manager.
  5. */
  6. public enum EnergyGoal {
  7. LOW(100d), MEDIUM(200d), HIGH(1000d);
  8. /**
  9. * The corresponding maximum energy in watt
  10. */
  11. private double maximumEnergyInRoom;
  12. /**
  13. * get the maximum energy consumption in each room
  14. *
  15. * @return the energy in watt
  16. */
  17. public double getMaximumEnergyInRoom() {
  18. return maximumEnergyInRoom;
  19. }
  20. private EnergyGoal(double powerInWatt) {
  21. maximumEnergyInRoom = powerInWatt;
  22. }
  23. }
package org.example.follow.me.manager;
 
/**
 * This enum describes the different energy goals associated with the
 * manager.
 */
public enum EnergyGoal {
    LOW(100d), MEDIUM(200d), HIGH(1000d);
 
    /**
     * The corresponding maximum energy in watt
     */
    private double maximumEnergyInRoom;
 
    /**
     * get the maximum energy consumption in each room
     * 
     * @return the energy in watt
     */
    public double getMaximumEnergyInRoom() {
        return maximumEnergyInRoom;
    }
 
    private EnergyGoal(double powerInWatt) {
        maximumEnergyInRoom = powerInWatt;
    }
}

Question 4 – Command: Write a command to be able to configure the energy saving goal and test your work.

FollowMeCommand

  1. g! setEnergyPreference MEDIUM
  2. g! getEnergyPreference
  3. EnergyMode = MEDIUM
g! setEnergyPreference MEDIUM
g! getEnergyPreference
EnergyMode = MEDIUM

Question 5 – Using DimmerLights: Change your implementation to take dimmer lights into account. One way to achieve this is to try to turn on as many binary lights as possible and then turn the DimmerLights to reach the targeted power by adjusting their powers.

Question 6 (optional) – Using heterogeneous lights: In the previous questions, we assumed that all the lights had the same nominal power consumption.

Now, you can try to write a more generic algorithm to manage heterogeneous lights.

First consider only the problem with BinaryLights. To do that, you might have to test all the combinations of BinaryLights. This can be achieved by solving the Subset sum problem.

The consumption of a light is given by the getMaxPowerLevel() method of the BinaryLight interface.

Here is a very naive implementation of this algorithm (feel free to implement your own) :

  1. package org.example.algorithm;
  2. import java.util.Arrays;
  3. import java.util.BitSet;
  4. /**
  5. * This class implements an algorithm that use a very naive approach of solving
  6. * the closest sum subset problem on an array of doubles.
  7. */
  8. public final class ClosestSumAlgorithm {
  9. /**
  10. * Find the subset of the items whose sum is closest to, without exceeding
  11. * maximalSum.
  12. *
  13. * Performance are low with doubles. Could largely be improved by using
  14. * integers instead. The algorithm is sufficiently effective for 15 lights
  15. * or less.
  16. *
  17. * @param maximalSum
  18. * the maximal sum of the subset;
  19. * @param items
  20. * an array containing the weight of each items. The order of
  21. * element will be preserved to produce the best
  22. * combination.
  23. * @return the subset of items whose sum is closest to maximalSum
  24. * without exceeding it. The combination is given in the same order
  25. * as the input array. array[i] contains the value of item[i] if it
  26. * is involved in the computing of the closest-sum, or 0 if not.
  27. */
  28. public static double[] greadySubSetClosestSum(final double maximalSum, final double[] items) {
  29. // the current best results :
  30. double bestSum = 0.0d;
  31. double[] bestCombination = new double[0];
  32. /*
  33. * Generate all the possible combinations. There are 2^N possibilities
  34. * that can therefore be represented by a bitset.
  35. * The use of bitset is done to reduce the number of line of codes.
  36. * The solution is thus far from being optimized.
  37. */
  38. for (int i = 0; i < Math.pow(2, items.length); i++) {
  39. // Get the current combination
  40. double[] currentCombination = multiplyByBitset(convertToBitSet(i), items);
  41. double currentSum = sum(currentCombination);
  42. // if we have the best result possible
  43. if (currentSum == maximalSum) {
  44. // return it
  45. return currentCombination;
  46. }
  47. // if the current result is better than the previous best result
  48. if ((currentSum <= maximalSum) && (currentSum > bestSum)) {
  49. // store it
  50. bestSum = currentSum;
  51. bestCombination = currentCombination;
  52. }
  53. }
  54. return bestCombination;
  55. }
  56. /**
  57. * Sum of the given variables or array.
  58. *
  59. * @param variables
  60. * the variables to be summed.
  61. * @return the sum of the variables.
  62. */
  63. private static double sum(double... variables) {
  64. double sum = 0;
  65. for (double var : variables) {
  66. sum += var;
  67. }
  68. return sum;
  69. }
  70. /**
  71. * Convert a number into BitSet.
  72. * This could be obtained directly in JAVA7 (iCASA is not compatible)
  73. *
  74. * @param number
  75. * the number to convert
  76. * @return the resulting bit set
  77. */
  78. private static BitSet convertToBitSet(long number) {
  79. BitSet bits = new BitSet();
  80. int index = 0;
  81. while (number != 0L) {
  82. if ((number % 2L) != 0) {
  83. bits.set(index);
  84. }
  85. ++index;
  86. number = number >>> 1;
  87. }
  88. return bits;
  89. }
  90. /**
  91. * Multiply an array by a bitset
  92. *
  93. * @param bitset
  94. * the BitSet
  95. * @param array
  96. * the array
  97. * @return the resulting array
  98. */
  99. private static double[] multiplyByBitset(BitSet bitset, double[] array) {
  100. assert (bitset.length() == array.length) : "array and bitset must have the same size";
  101. double[] result = new double[array.length];
  102. for (int i = 0; i < array.length; i++) {
  103. result[i] = bitset.get(i) ? array[i] : 0;
  104. }
  105. return result;
  106. }
  107. }
package org.example.algorithm;
 
import java.util.Arrays;
import java.util.BitSet;
 
/**
 * This class implements an algorithm that use a very naive approach of solving
 * the closest sum subset problem on an array of doubles.
 */
public final class ClosestSumAlgorithm {
 
    /**
     * Find the subset of the items whose sum is closest to, without exceeding
     * maximalSum.
     * 
     * Performance are low with doubles. Could largely be improved by using
     * integers instead. The algorithm is sufficiently effective for 15 lights
     * or less.
     * 
     * @param maximalSum
     *            the maximal sum of the subset;
     * @param items
     *            an array containing the weight of each items. The order of
     *            element will be preserved to produce the best
     *            combination.
     * @return the subset of items whose sum is closest to maximalSum
     *         without exceeding it. The combination is given in the same order
     *         as the input array. array[i] contains the value of item[i] if it
     *         is involved in the computing of the closest-sum, or 0 if not.
     */
    public static double[] greadySubSetClosestSum(final double maximalSum, final double[] items) {
 
        // the current best results :
        double bestSum = 0.0d;
        double[] bestCombination = new double[0];
 
        /*
         * Generate all the possible combinations. There are 2^N possibilities
         * that can therefore be represented by a bitset.
         * The use of bitset is done to reduce the number of line of codes.
         * The solution is thus far from being optimized.
         */
        for (int i = 0; i < Math.pow(2, items.length); i++) {
            // Get the current combination 
            double[] currentCombination = multiplyByBitset(convertToBitSet(i), items);
            double currentSum = sum(currentCombination);
 
            // if we have the best result possible
            if (currentSum == maximalSum) {
                // return it
                return currentCombination;
            }
 
            // if the current result is better than the previous best result
            if ((currentSum <= maximalSum) && (currentSum > bestSum)) {
                // store it
                bestSum = currentSum;
                bestCombination = currentCombination;
            }
        }
 
        return bestCombination;
    }
 
    /**
     * Sum of the given variables or array.
     * 
     * @param variables
     *            the variables to be summed.
     * @return the sum of the variables.
     */
    private static double sum(double... variables) {
        double sum = 0;
        for (double var : variables) {
            sum += var;
        }
        return sum;
    }
 
    /**
     * Convert a number into BitSet.
     * This could be obtained directly in JAVA7 (iCASA is not compatible)
     * 
     * @param number
     *            the number to convert
     * @return the resulting bit set
     */
    private static BitSet convertToBitSet(long number) {
        BitSet bits = new BitSet();
        int index = 0;
        while (number != 0L) {
            if ((number % 2L) != 0) {
                bits.set(index);
            }
            ++index;
            number = number >>> 1;
        }
        return bits;
    }
 
    /**
     * Multiply an array by a bitset
     * 
     * @param bitset
     *            the BitSet
     * @param array
     *            the array
     * @return the resulting array
     */
    private static double[] multiplyByBitset(BitSet bitset, double[] array) {
        assert (bitset.length() == array.length) : "array and bitset must have the same size";
 
        double[] result = new double[array.length];
        for (int i = 0; i < array.length; i++) {
            result[i] = bitset.get(i) ? array[i] : 0;
        }
        return result;
    }
}

Example of use :

  1. public static void main(String[] args) {
  2. double[] items = new double[] { 1.5d, 7.4d, 3.4d, 8.3d, 15.233d, 99d, 22d, 76d, 38d, 22d, 7d, 0.10d, 54.9d, 45.9d, 90d, 48.6d, 6.1d, 4.2d, 89.3d };
  3. // Targeted sum :
  4. double maxSum = 99.97484;
  5. // Compute the best combination :
  6. double[] result = ClosestSumAlgorithm.greadySubSetClosestSum(maxSum, items);
  7. System.out.println(Arrays.toString(result));
  8. System.out.println(sum(result));
  9. }
public
 static void main(String[] args) {
    double[] items = new double[] { 1.5d, 7.4d, 3.4d, 8.3d, 15.233d, 
99d, 22d, 76d, 38d, 22d, 7d, 0.10d, 54.9d, 45.9d, 90d, 48.6d, 6.1d, 
4.2d, 89.3d };
 
    // Targeted sum :
    double maxSum = 99.97484;
    // Compute the best combination :
    double[] result = ClosestSumAlgorithm.greadySubSetClosestSum(maxSum,
 items);
    System.out.println(Arrays.toString(result));
    System.out.println(sum(result));
 
}

Then you may try to find a general solution for environment including some dimmer lights.