From 4ad3cfa1181012dae749d113ed4355f7dd3b017f Mon Sep 17 00:00:00 2001 From: avelichk Date: Sat, 31 Oct 2020 03:38:11 +0000 Subject: [PATCH] Add early stopping section to submit Experiment page --- .../frontend/src/actions/generalActions.js | 29 +++ .../Common/Create/Params/EarlyStopping.jsx | 165 ++++++++++++++++++ .../src/components/HP/Create/HPParameters.jsx | 38 ++++ .../src/components/HP/Monitor/HPJobTable.jsx | 2 +- .../v1beta1/frontend/src/reducers/general.js | 32 ++++ 5 files changed, 265 insertions(+), 1 deletion(-) create mode 100644 pkg/ui/v1beta1/frontend/src/components/Common/Create/Params/EarlyStopping.jsx diff --git a/pkg/ui/v1beta1/frontend/src/actions/generalActions.js b/pkg/ui/v1beta1/frontend/src/actions/generalActions.js index 8407fe238b9..5b6b6af5fb2 100644 --- a/pkg/ui/v1beta1/frontend/src/actions/generalActions.js +++ b/pkg/ui/v1beta1/frontend/src/actions/generalActions.js @@ -115,6 +115,35 @@ export const changeStatus = (filter, checked) => ({ checked, }); +export const CHANGE_EARLY_STOPPING_ALGORITHM = 'CHANGE_EARLY_STOPPING_ALGORITHM'; + +export const changeEarlyStoppingAlgorithm = algorithmName => ({ + type: CHANGE_EARLY_STOPPING_ALGORITHM, + algorithmName, +}); + +export const ADD_EARLY_STOPPING_SETTING = 'ADD_EARLY_STOPPING_SETTING'; + +export const addEarlyStoppingSetting = () => ({ + type: ADD_EARLY_STOPPING_SETTING, +}); + +export const CHANGE_EARLY_STOPPING_SETTING = 'CHANGE_EARLY_STOPPING_SETTING'; + +export const changeEarlyStoppingSetting = (index, field, value) => ({ + type: CHANGE_EARLY_STOPPING_SETTING, + index, + field, + value, +}); + +export const DELETE_EARLY_STOPPING_SETTING = 'DELETE_EARLY_STOPPING_SETTING'; + +export const deleteEarlyStoppingSetting = index => ({ + type: DELETE_EARLY_STOPPING_SETTING, + index, +}); + export const CHANGE_TRIAL_TEMPLATE_SOURCE = 'CHANGE_TRIAL_TEMPLATE_SOURCE'; export const changeTrialTemplateSource = source => ({ diff --git a/pkg/ui/v1beta1/frontend/src/components/Common/Create/Params/EarlyStopping.jsx b/pkg/ui/v1beta1/frontend/src/components/Common/Create/Params/EarlyStopping.jsx new file mode 100644 index 00000000000..43117e16d3e --- /dev/null +++ b/pkg/ui/v1beta1/frontend/src/components/Common/Create/Params/EarlyStopping.jsx @@ -0,0 +1,165 @@ +import React from 'react'; +import { connect } from 'react-redux'; + +import { makeStyles } from '@material-ui/core/styles'; +import Button from '@material-ui/core/Button'; +import Grid from '@material-ui/core/Grid'; +import Tooltip from '@material-ui/core/Tooltip'; +import HelpOutlineIcon from '@material-ui/icons/HelpOutline'; +import Typography from '@material-ui/core/Typography'; +import MenuItem from '@material-ui/core/MenuItem'; +import FormControl from '@material-ui/core/FormControl'; +import Select from '@material-ui/core/Select'; +import InputLabel from '@material-ui/core/InputLabel'; +import TextField from '@material-ui/core/TextField'; +import IconButton from '@material-ui/core/IconButton'; +import DeleteIcon from '@material-ui/icons/Delete'; + +import { + changeEarlyStoppingAlgorithm, + addEarlyStoppingSetting, + changeEarlyStoppingSetting, + deleteEarlyStoppingSetting, +} from '../../../../actions/generalActions'; + +import { GENERAL_MODULE } from '../../../../constants/constants'; + +const useStyles = makeStyles({ + textField: { + width: '80%', + }, + help: { + padding: 4 / 2, + verticalAlign: 'middle', + marginRight: 5, + }, + parameter: { + padding: 2, + marginBottom: 10, + }, + icon: { + padding: 4, + margin: '0 auto', + verticalAlign: 'middle !important', + }, + formControl: { + width: '100%', + }, + addButton: { + margin: 10, + }, +}); + +const EarlyStopping = props => { + const classes = useStyles(); + + const onEarlyStoppingAlgorithmChange = event => { + props.changeEarlyStoppingAlgorithm(event.target.value); + }; + + const onAddEarlyStoppingSetting = () => { + props.addEarlyStoppingSetting(); + }; + + const onChangeEarlyStoppingSetting = (field, index) => event => { + props.changeEarlyStoppingSetting(index, field, event.target.value); + }; + + const onDeleteEarlyStoppingSetting = index => event => { + props.deleteEarlyStoppingSetting(index); + }; + return ( +
+ +
+ + + + + + + {'Early Stopping Algorithm Name'} + + + + + Algorithm Name + + + + +
+
+ {props.earlyStoppingSettings.map((setting, i) => { + return ( +
+ + + + + + + + + + + + + + +
+ ); + })} +
+ ); +}; + +const mapStateToProps = state => { + return { + earlyStoppingAlgorithm: state[GENERAL_MODULE].earlyStoppingAlgorithm, + allEarlyStoppingAlgorithms: state[GENERAL_MODULE].allEarlyStoppingAlgorithms, + earlyStoppingSettings: state[GENERAL_MODULE].earlyStoppingSettings, + }; +}; + +export default connect(mapStateToProps, { + changeEarlyStoppingAlgorithm, + addEarlyStoppingSetting, + changeEarlyStoppingSetting, + deleteEarlyStoppingSetting, +})(EarlyStopping); diff --git a/pkg/ui/v1beta1/frontend/src/components/HP/Create/HPParameters.jsx b/pkg/ui/v1beta1/frontend/src/components/HP/Create/HPParameters.jsx index 1e5004372f0..4608676f403 100644 --- a/pkg/ui/v1beta1/frontend/src/components/HP/Create/HPParameters.jsx +++ b/pkg/ui/v1beta1/frontend/src/components/HP/Create/HPParameters.jsx @@ -14,7 +14,10 @@ import Objective from './Params/Objective'; import TrialTemplate from '../../Common/Create/Params/Trial/TrialTemplate'; import Parameters from './Params/Parameters'; import Algorithm from './Params/Algorithm'; +import EarlyStopping from '../../Common/Create/Params/EarlyStopping'; import MetricsCollectorSpec from '../../Common/Create/Params/MetricsCollector'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; +import Checkbox from '@material-ui/core/Checkbox'; import { submitHPJob } from '../../../actions/hpCreateActions'; @@ -110,6 +113,14 @@ const HPParameters = props => { data.spec.algorithm.algorithmSettings = []; addAlgorithmSettings(props.algorithmSettings, data.spec.algorithm.algorithmSettings); + // Add early stopping if selected. + if (checkedSetEarlyStopping) { + data.spec.earlyStopping = {}; + data.spec.earlyStopping.algorithmName = props.earlyStoppingAlgorithm; + data.spec.earlyStopping.algorithmSettings = []; + addAlgorithmSettings(props.earlyStoppingSettings, data.spec.earlyStopping.algorithmSettings); + } + data.spec.parameters = []; addParameter(props.parameters, data.spec.parameters); @@ -238,6 +249,12 @@ const HPParameters = props => { const { classes } = props; + const [checkedSetEarlyStopping, setCheckedSetEarlyStopping] = React.useState(false); + + const onCheckBoxChange = event => { + setCheckedSetEarlyStopping(event.target.checked); + }; + return (
{/* Common Metadata */} @@ -251,6 +268,25 @@ const HPParameters = props => { {SectionInTypography('Algorithm')} + + + Early Stopping (Optional) + + + + } + label="Set" + /> + + + {checkedSetEarlyStopping && } + {SectionInTypography('Parameters')} {SectionInTypography('Metrics Collector Spec')} @@ -290,6 +326,8 @@ const mapStateToProps = state => { additionalMetricNames: state[constants.HP_CREATE_MODULE].additionalMetricNames, metricStrategies: state[constants.HP_CREATE_MODULE].metricStrategies, algorithmName: state[constants.HP_CREATE_MODULE].algorithmName, + earlyStoppingAlgorithm: state[constants.GENERAL_MODULE].earlyStoppingAlgorithm, + earlyStoppingSettings: state[constants.GENERAL_MODULE].earlyStoppingSettings, algorithmSettings: state[constants.HP_CREATE_MODULE].algorithmSettings, parameters: state[constants.HP_CREATE_MODULE].parameters, primaryPodLabels: state[constants.GENERAL_MODULE].primaryPodLabels, diff --git a/pkg/ui/v1beta1/frontend/src/components/HP/Monitor/HPJobTable.jsx b/pkg/ui/v1beta1/frontend/src/components/HP/Monitor/HPJobTable.jsx index f6deb2ae2fc..d04c5e79767 100644 --- a/pkg/ui/v1beta1/frontend/src/components/HP/Monitor/HPJobTable.jsx +++ b/pkg/ui/v1beta1/frontend/src/components/HP/Monitor/HPJobTable.jsx @@ -115,7 +115,7 @@ class HPJobTable extends React.Component { return ( - {this.props.data.length > 1 && ( + {this.props.data.length >= 1 && (
diff --git a/pkg/ui/v1beta1/frontend/src/reducers/general.js b/pkg/ui/v1beta1/frontend/src/reducers/general.js index 5a317544f47..8c0367c58ff 100644 --- a/pkg/ui/v1beta1/frontend/src/reducers/general.js +++ b/pkg/ui/v1beta1/frontend/src/reducers/general.js @@ -32,6 +32,10 @@ const initialState = { suggestion: {}, dialogSuggestionOpen: false, + earlyStoppingAlgorithm: 'medianstop', + allEarlyStoppingAlgorithms: ['medianstop'], + earlyStoppingSettings: [], + trialTemplateSourceList: [TEMPLATE_SOURCE_CONFIG_MAP, TEMPLATE_SOURCE_YAML], trialTemplateSource: 'ConfigMap', primaryPodLabels: [], @@ -237,6 +241,34 @@ const generalReducer = (state = initialState, action) => { dialogExperimentOpen: false, dialogSuggestionOpen: false, }; + // Experiment early stopping actions. + case actions.CHANGE_EARLY_STOPPING_ALGORITHM: + return { + ...state, + earlyStoppingAlgorithm: action.algorithmName, + }; + case actions.ADD_EARLY_STOPPING_SETTING: + var earlyStoppingSettings = state.earlyStoppingSettings.slice(); + let setting = { name: '', value: '' }; + earlyStoppingSettings.push(setting); + return { + ...state, + earlyStoppingSettings: earlyStoppingSettings, + }; + case actions.CHANGE_EARLY_STOPPING_SETTING: + earlyStoppingSettings = state.earlyStoppingSettings.slice(); + earlyStoppingSettings[action.index][action.field] = action.value; + return { + ...state, + earlyStoppingSettings: earlyStoppingSettings, + }; + case actions.DELETE_EARLY_STOPPING_SETTING: + earlyStoppingSettings = state.earlyStoppingSettings.slice(); + earlyStoppingSettings.splice(action.index, 1); + return { + ...state, + earlyStoppingSettings: earlyStoppingSettings, + }; // Experiment Trial Template actions. case templateActions.FETCH_TRIAL_TEMPLATES_SUCCESS: var trialTemplatesData = action.trialTemplatesData;