Skip to content

Commit

Permalink
fix: invalid schedule (#30)
Browse files Browse the repository at this point in the history
  • Loading branch information
enell authored May 5, 2023
1 parent 1810c13 commit 44aec53
Show file tree
Hide file tree
Showing 5 changed files with 183 additions and 50 deletions.
23 changes: 10 additions & 13 deletions src/fitness.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ const calculateNormalPeriod = (g1, g2) => {
}
}

function* allPeriodsGenerator(props, excessPvEnergyUse, p) {
function* allPeriodsGenerator(props, phenotype) {
const { batteryMaxEnergy, soc, totalDuration } = props
const { excessPvEnergyUse, periods } = phenotype
let currentCharge = soc * batteryMaxEnergy

const addCosts = (period) => {
Expand All @@ -45,26 +46,26 @@ function* allPeriodsGenerator(props, excessPvEnergyUse, p) {
return period
}

for (let i = 0; i < p.length; i += 1) {
for (let i = 0; i < periods.length; i += 1) {
const normalPeriod = calculateNormalPeriod(
p[i - 1] ?? { start: 0, duration: 0 },
p[i]
periods[i - 1] ?? { start: 0, duration: 0 },
periods[i]
)
if (normalPeriod.duration > 0) yield addCosts(normalPeriod)
yield addCosts(p[i])
yield addCosts(periods[i])
}

const normalPeriod = calculateNormalPeriod(
p.at(-1) ?? { start: 0, duration: 0 },
periods.at(-1) ?? { start: 0, duration: 0 },
{
start: totalDuration,
}
)
if (normalPeriod.duration > 0) yield addCosts(normalPeriod)
}

const allPeriods = (props, excessPvEnergyUse, p) => {
return [...allPeriodsGenerator(props, excessPvEnergyUse, p)]
const allPeriods = (props, phenotype) => {
return [...allPeriodsGenerator(props, phenotype)]
}

const FEED_TO_GRID = 0
Expand Down Expand Up @@ -203,11 +204,7 @@ const calculatePeriodScore = (
const fitnessFunction = (props) => (phenotype) => {
let cost = 0

for (const period of allPeriodsGenerator(
props,
phenotype.excessPvEnergyUse,
phenotype.periods
)) {
for (const period of allPeriodsGenerator(props, phenotype)) {
cost -= period.cost
}

Expand Down
39 changes: 14 additions & 25 deletions src/strategy-battery-charging-functions.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
const geneticalgorithm = require('geneticalgorithm')
const geneticAlgorithmConstructor = require('geneticalgorithm')
const {
fitnessFunction,
allPeriodsGenerator,
calculatePeriodScore,
} = require('./fitness')
const { fitnessFunction, allPeriodsGenerator } = require('./fitness')

const random = (min, max) => {
return Math.floor(Math.random() * (max - min)) + min
Expand All @@ -15,13 +12,7 @@ const clamp = (num, min, max) => {

const repair = (phenotype, totalDuration) => {
const trimGene = (gene) => {
if (gene.start < 0) {
gene.duration += Math.max(gene.start, gene.duration * -1)
gene.start = 0
}
if (gene.start > totalDuration) {
gene.start = totalDuration - 1
}
gene.start = clamp(gene.start, 0, totalDuration - 1)
gene.duration = clamp(gene.duration, 0, totalDuration - gene.start)
}

Expand All @@ -38,7 +29,7 @@ const repair = (phenotype, totalDuration) => {
const adjustment = (diff / 2) * -1
g1.duration -= clamp(Math.ceil(adjustment), 0, g1.duration)
g2.start += Math.floor(adjustment)
g2.duration -= clamp(Math.ceil(adjustment), 0, g2.duration)
g2.duration -= clamp(Math.floor(adjustment), 0, g2.duration)
}
}
return p
Expand Down Expand Up @@ -167,16 +158,12 @@ const toSchedule = (props, phenotype) => {
const schedule = []
//props, totalDuration, excessPvEnergyUse, p
const periodStart = new Date(input[0].start)
for (const period of allPeriodsGenerator(
props,
phenotype.excessPvEnergyUse,
phenotype.periods
)) {
for (const period of allPeriodsGenerator(props, phenotype)) {
if (period.duration <= 0) {
continue
}

if (schedule.length && period.activity === schedule.at(-1).activity) {
if (schedule.length && period.activity == schedule.at(-1).activity) {
schedule.at(-1).duration += period.duration
schedule.at(-1).cost += period.cost
schedule.at(-1).charge += period.charge
Expand Down Expand Up @@ -241,13 +228,14 @@ const calculateBatteryChargingStrategy = (config) => {
totalDuration: input.length * 60,
}

const f = fitnessFunction(props)
const geneticAlgorithm = geneticAlgorithmConstructor({
const options = {
mutationFunction: mutationFunction(props),
crossoverFunction: crossoverFunction(props),
fitnessFunction: f,
fitnessFunction: fitnessFunction(props),
population: generatePopulation(props),
})
}

const geneticAlgorithm = geneticAlgorithmConstructor(options)

for (let i = 0; i < generations; i += 1) {
geneticAlgorithm.evolve()
Expand All @@ -259,18 +247,19 @@ const calculateBatteryChargingStrategy = (config) => {
best: {
schedule: toSchedule(props, best),
excessPvEnergyUse: best.excessPvEnergyUse,
cost: f(best) * -1,
cost: options.fitnessFunction(best) * -1,
},
noBattery: {
schedule: toSchedule(props, noBattery),
excessPvEnergyUse: noBattery.excessPvEnergyUse,
cost: f(noBattery) * -1,
cost: options.fitnessFunction(noBattery) * -1,
},
}
}

module.exports = {
clamp,
repair,
crossoverFunction,
mutationFunction,
fitnessFunction,
Expand Down
32 changes: 22 additions & 10 deletions test/fitness.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,20 +96,26 @@ describe('Fitness - splitIntoHourIntervals', () => {

describe('Fitness - allPeriods', () => {
test('should test allPeriods empty', () => {
expect(allPeriods(props, 0, [])).toMatchObject([
{ start: 0, duration: 300, activity: 0 },
])
expect(
allPeriods(props, { excessPvEnergyUse: 0, periods: [] })
).toMatchObject([{ start: 0, duration: 300, activity: 0 }])
})

test('should test allPeriods one activity', () => {
expect(
allPeriods(props, 0, [{ start: 0, duration: 300, activity: 1 }])
allPeriods(props, {
excessPvEnergyUse: 0,
periods: [{ start: 0, duration: 300, activity: 1 }],
})
).toMatchObject([{ start: 0, duration: 300, activity: 1 }])
})

test('should test allPeriods one in the middle', () => {
expect(
allPeriods(props, 0, [{ start: 120, duration: 60, activity: 1 }])
allPeriods(props, {
excessPvEnergyUse: 0,
periods: [{ start: 120, duration: 60, activity: 1 }],
})
).toMatchObject([
{ start: 0, duration: 120, activity: 0 },
{ start: 120, duration: 60, activity: 1 },
Expand All @@ -119,7 +125,10 @@ describe('Fitness - allPeriods', () => {

test('should test allPeriods one long activity', () => {
expect(
allPeriods(props, 0, [{ start: 100, duration: 100, activity: 1 }])
allPeriods(props, {
excessPvEnergyUse: 0,
periods: [{ start: 100, duration: 100, activity: 1 }],
})
).toMatchObject([
{ start: 0, duration: 100, activity: 0 },
{ start: 100, duration: 100, activity: 1 },
Expand All @@ -129,10 +138,13 @@ describe('Fitness - allPeriods', () => {

test('should test allPeriods two activities', () => {
expect(
allPeriods(props, 0, [
{ start: 70, activity: 1, duration: 80 },
{ start: 160, activity: -1, duration: 30 },
])
allPeriods(props, {
excessPvEnergyUse: 0,
periods: [
{ start: 70, activity: 1, duration: 80 },
{ start: 160, activity: -1, duration: 30 },
],
})
).toMatchObject([
{ start: 0, duration: 70, activity: 0 },
{ start: 70, duration: 80, activity: 1 },
Expand Down
2 changes: 1 addition & 1 deletion test/strategy-battery-charging-functions-mutate.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ describe('Mutation', () => {

expect(p).toMatchObject({
periods: [
{ start: 0, activity: -1, duration: 5 },
{ start: 0, activity: -1, duration: 10 },
{ start: 85, activity: 1, duration: 10 },
],
excessPvEnergyUse: 0,
Expand Down
137 changes: 136 additions & 1 deletion test/strategy-battery-charging-functions.test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
const { expect } = require('@jest/globals')
const { expect, describe, it } = require('@jest/globals')
const { mockRandomForEach } = require('jest-mock-random')
const {
clamp,
repair,
calculateBatteryChargingStrategy,
crossoverFunction,
} = require('../src/strategy-battery-charging-functions')
Expand Down Expand Up @@ -163,3 +164,137 @@ describe('Calculate', () => {
}, [])
})
})

describe('Repair', () => {
it('Repair - one valid gene', () => {
const p = [{ start: 5, duration: 10 }]
expect(repair(p, 20)).toEqual(p)
})

it('Repair - one valid gene filling all', () => {
const p = [{ start: 0, duration: 10 }]
expect(repair(p, 10)).toEqual(p)
})

it('Repair - two valid genes', () => {
const p = [
{ start: 5, duration: 10 },
{ start: 20, duration: 10 },
]
expect(repair(p, 50)).toEqual(p)
})

it('Repair - two valid genes in wrong order', () => {
const p = [
{ start: 20, duration: 10 },
{ start: 5, duration: 10 },
]
expect(repair(p, 50)).toEqual([
{ start: 5, duration: 10 },
{ start: 20, duration: 10 },
])
})

it('Repair - two genes next to each other', () => {
const p = [
{ start: 5, duration: 10 },
{ start: 15, duration: 10 },
]
expect(repair(p, 50)).toEqual([
{ start: 5, duration: 10 },
{ start: 15, duration: 10 },
])
})

it('Repair - two genes just crossing', () => {
const p = [
{ start: 5, duration: 10 },
{ start: 14, duration: 10 },
]
expect(repair(p, 50)).toEqual([
{ start: 5, duration: 9 },
{ start: 14, duration: 10 },
])
})

it('Repair - two genes crossing', () => {
const p = [
{ start: 5, duration: 10 },
{ start: 10, duration: 10 },
]
expect(repair(p, 50)).toEqual([
{ start: 5, duration: 7 },
{ start: 12, duration: 8 },
])
})

it('Repair - three genes crossing', () => {
const p = [
{ start: 5, duration: 10 },
{ start: 10, duration: 10 },
{ start: 16, duration: 10 },
]
expect(repair(p, 50)).toEqual([
{ start: 5, duration: 7 },
{ start: 12, duration: 6 },
{ start: 18, duration: 8 },
])
})

it('Repair - two genes completely overlapping', () => {
const p = [
{ start: 5, duration: 10 },
{ start: 5, duration: 10 },
]
expect(repair(p, 50)).toEqual([
{ start: 5, duration: 5 },
{ start: 10, duration: 5 },
])
})

it('Repair - three genes completely overlapping', () => {
const p = [
{ start: 5, duration: 10 },
{ start: 5, duration: 10 },
{ start: 5, duration: 10 },
]
expect(repair(p, 50)).toEqual([
{ start: 5, duration: 5 },
{ start: 10, duration: 0 },
{ start: 10, duration: 5 },
])
})

it('Repair - start lower than 0', () => {
const p = [
{ start: -5, duration: 10 },
{ start: 20, duration: 10 },
]
expect(repair(p, 50)).toEqual([
{ start: 0, duration: 10 },
{ start: 20, duration: 10 },
])
})

it('Repair - duration higher than max', () => {
const p = [
{ start: 0, duration: 10 },
{ start: 45, duration: 10 },
]
expect(repair(p, 50)).toEqual([
{ start: 0, duration: 10 },
{ start: 45, duration: 5 },
])
})

it('Repair - start higher than max', () => {
const p = [
{ start: 0, duration: 10 },
{ start: 55, duration: 10 },
]
expect(repair(p, 50)).toEqual([
{ start: 0, duration: 10 },
{ start: 49, duration: 1 },
])
})
})

0 comments on commit 44aec53

Please sign in to comment.