Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Calculate suggestion count #329

Merged
merged 8 commits into from
Sep 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/clean-mails-accept.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@boostv/process-optimizer-frontend-core': patch
'@boostv/process-optimizer-frontend-ui': patch
---

Improve calculation of the number of experiments to suggest
7 changes: 6 additions & 1 deletion packages/core/src/context/experiment/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
calculateSpace,
experimentResultSchema,
} from '@core/common'
import { selectCalculatedSuggestionCountFromExperiment } from './experiment-selectors'

export const createFetchExperimentResultRequest = (
experiment: ExperimentType
Expand All @@ -25,7 +26,11 @@ export const createFetchExperimentResultRequest = (
experiment.scoreVariables,
experiment.dataPoints
),
extras: extras,
extras: {
...extras,
experimentSuggestionCount:
selectCalculatedSuggestionCountFromExperiment(experiment),
},
optimizerConfig: {
acqFunc: cfg.acqFunc,
baseEstimator: cfg.baseEstimator,
Expand Down
20 changes: 1 addition & 19 deletions packages/core/src/context/experiment/experiment-reducers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,17 +63,6 @@ const calculateXi = (state: ExperimentType) => {
)
}

const calculateExperimentSuggestionCount = (state: ExperimentType) => {
const dataPoints = state.dataPoints.filter(
d => d.meta.enabled && d.meta.valid
).length
const initialPoints = state.optimizerConfig.initialPoints
if (dataPoints >= initialPoints) {
return 1
}
return initialPoints
}

export type ExperimentAction =
| {
type: 'setSwVersion'
Expand Down Expand Up @@ -371,8 +360,6 @@ export const experimentReducer = produce(
state.optimizerConfig = experimentSchema.shape.optimizerConfig.parse(
action.payload
)
state.extras.experimentSuggestionCount =
calculateExperimentSuggestionCount(state)
break
case 'registerResult':
state.lastEvaluationHash = md5(
Expand All @@ -388,8 +375,6 @@ export const experimentReducer = produce(
state.scoreVariables,
action.payload
)
state.extras.experimentSuggestionCount =
calculateExperimentSuggestionCount(state)
state.optimizerConfig.xi = calculateXi(state)
break
case 'experiment/toggleMultiObjective':
Expand Down Expand Up @@ -441,8 +426,6 @@ export const experimentReducer = produce(
dimensions: [action.payload],
})
}
state.extras.experimentSuggestionCount =
calculateExperimentSuggestionCount(state)
break
}
case 'experiment/removeVariableFromConstraintSum': {
Expand All @@ -452,8 +435,7 @@ export const experimentReducer = produce(
d => d !== action.payload
)
}
state.extras.experimentSuggestionCount =
calculateExperimentSuggestionCount(state)

break
}
default:
Expand Down
116 changes: 116 additions & 0 deletions packages/core/src/context/experiment/experiment-selectors.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import { initialState, State } from '@core/context/experiment/store'
import {
selectCalculatedSuggestionCount,
selectId,
selectIsConstraintActive,
selectIsInitializing,
selectIsMultiObjective,
selectIsSuggestionCountEditable,
} from './experiment-selectors'
import { rootReducer } from './reducers'
import { createDataPoints } from './test-utils'
import { ExperimentType } from '@core/common'

describe('Experiment selectors', () => {
let state: State
Expand Down Expand Up @@ -59,4 +64,115 @@ describe('Experiment selectors', () => {
expect(after2ndToggle).toEqual(before)
})
})

describe('selectIsSuggestionCountEditable', () => {
const constraints: ExperimentType['constraints'] = [
{
type: 'sum',
dimensions: ['a', 'b'],
value: 100,
},
]
it.each([
//data points < initial points, constraint active
[1, 2, constraints, true],
//data points < initial points, constraint NOT active
[1, 2, [], true],
//data points = initial points, constraint active
[1, 1, constraints, false],
//data points = initial points, constraint NOT active
[1, 1, [], true],
//data points > initial points, constraint active
[2, 1, constraints, false],
//data points > initial points, constraint NOT active
[2, 1, [], true],
])(
'should be true when data points < intial points or constraints are active',
(dataPoints, initialPoints, constraints, result) => {
const editable = selectIsSuggestionCountEditable({
...initialState,
experiment: {
...initialState.experiment,
dataPoints: createDataPoints(dataPoints),
optimizerConfig: {
...initialState.experiment.optimizerConfig,
initialPoints,
},
constraints,
},
})
expect(editable).toBe(result)
}
)
})

describe('selectCalculatedSuggestionCount', () => {
const suggestionCount = 5
const constraints: ExperimentType['constraints'] = [
{
type: 'sum',
dimensions: ['a', 'b'],
value: 100,
},
]
it.each([
//data points < initial points, constraint active -> initial points
[1, 2, constraints, 2],
//data points < initial points, constraint NOT active -> initial points
[1, 2, [], 2],
//data points = initial points, constraint active -> 1
[1, 1, constraints, 1],
//data points = initial points, constraint NOT active -> suggestionCount
[1, 1, [], suggestionCount],
//data points > initial points, constraint active -> 1
[2, 1, constraints, 1],
//data points > initial points, constraint NOT active -> suggestionCount
[2, 1, [], suggestionCount],
])(
'should return correct value',
(dataPoints, initialPoints, constraints, result) => {
const editable = selectCalculatedSuggestionCount({
...initialState,
experiment: {
...initialState.experiment,
dataPoints: createDataPoints(dataPoints),
optimizerConfig: {
...initialState.experiment.optimizerConfig,
initialPoints,
},
constraints,
extras: {
...initialState.experiment.extras,
experimentSuggestionCount: suggestionCount,
},
},
})
expect(editable).toBe(result)
}
)
})

describe('selectIsConstraintActive', () => {
it.each([
[['a', 'b', 'c'], true],
[['a', 'b'], true],
[['a'], false],
[[], false],
])(
'should return true when number of sum constraint variables > 1',
(dimensions, result) => {
const editable = selectIsConstraintActive({
...initialState.experiment,
constraints: [
{
type: 'sum',
value: 100,
dimensions,
},
],
})
expect(editable).toBe(result)
}
)
})
})
55 changes: 51 additions & 4 deletions packages/core/src/context/experiment/experiment-selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ export const selectDataPoints = (state: State) =>
selectExperiment(state).dataPoints

export const selectActiveDataPoints = (state: State) =>
selectExperiment(state).dataPoints.filter(d => d.meta.valid && d.meta.enabled)
selectActiveDataPointsFromExperiment(selectExperiment(state))

export const selectActiveDataPointsFromExperiment = (
experiment: ExperimentType
) => experiment.dataPoints.filter(d => d.meta.valid && d.meta.enabled)

export const selectExpectedMinimum = (state: State) =>
selectExperiment(state).results.expectedMinimum
Expand Down Expand Up @@ -51,7 +55,50 @@ export const selectActiveVariableNames = (state: State): string[] => {
)
}

export const selectSumConstraint = (state: State) => {
const experiment = selectExperiment(state)
return experiment.constraints.find(c => c.type === 'sum')
export const selectSumConstraint = (state: State) =>
selectSumConstraintFromExperiment(selectExperiment(state))

export const selectSumConstraintFromExperiment = (experiment: ExperimentType) =>
experiment.constraints.find(c => c.type === 'sum')

export const selectIsConstraintActive = (experiment: ExperimentType) =>
(selectSumConstraintFromExperiment(experiment)?.dimensions.length ?? 0) > 1

export const selectInitialPoints = (state: State) =>
selectInitialPointsFromExperiment(selectExperiment(state))

export const selectInitialPointsFromExperiment = (experiment: ExperimentType) =>
experiment.optimizerConfig.initialPoints

export const selectIsSuggestionCountEditable = (state: State) => {
const dataPoints = selectActiveDataPoints(state)
const initialPoints = selectInitialPoints(state)
return (
dataPoints.length < initialPoints ||
!selectIsConstraintActive(selectExperiment(state))
)
}

export const selectSuggestionCountFromExperiment = (
experiment: ExperimentType
) =>
Number.isInteger(experiment.extras['experimentSuggestionCount'])
? Number(experiment.extras['experimentSuggestionCount'])
: 1

export const selectCalculatedSuggestionCount = (state: State) =>
selectCalculatedSuggestionCountFromExperiment(selectExperiment(state))

export const selectCalculatedSuggestionCountFromExperiment = (
experiment: ExperimentType
) => {
const dataPoints = selectActiveDataPointsFromExperiment(experiment).length
const initialPoints = selectInitialPointsFromExperiment(experiment)

if (dataPoints < initialPoints) {
return initialPoints
} else if (selectIsConstraintActive(experiment)) {
return 1
}
return selectSuggestionCountFromExperiment(experiment)
}
Loading
Loading