Unverified Commit 33cdb5b6 authored by Lijiaoa's avatar Lijiaoa Committed by GitHub
Browse files

Bug fix: after clicking compare button, the dropdown button (for selecting...

Bug fix: after clicking compare button, the dropdown button (for selecting different keys) is not shown (#4990)
parent 60e1e01f
...@@ -26,7 +26,7 @@ const OpenRow = (props): any => { ...@@ -26,7 +26,7 @@ const OpenRow = (props): any => {
const trialId = props.trialId; const trialId = props.trialId;
const trial = TRIALS.getTrial(trialId); const trial = TRIALS.getTrial(trialId);
const logPathRow = trial.info.logPath || "This trial's log path is not available."; const logPathRow = trial.info.logPath || "This trial's log path is not available.";
const originParameters = trial.description.parameters; const originParameters = trial.parameter;
const hasVisualHyperParams = RETIARIIPARAMETERS in originParameters; const hasVisualHyperParams = RETIARIIPARAMETERS in originParameters;
const hideMessageInfo = (): void => { const hideMessageInfo = (): void => {
...@@ -55,7 +55,7 @@ const OpenRow = (props): any => { ...@@ -55,7 +55,7 @@ const OpenRow = (props): any => {
const copyParams = (trial: Trial): void => { const copyParams = (trial: Trial): void => {
// get copy parameters // get copy parameters
const params = JSON.stringify(reformatRetiariiParameter(trial.description.parameters as any), null, 4); const params = JSON.stringify(reformatRetiariiParameter(trial.parameter as any), null, 4);
if (copy.default(params)) { if (copy.default(params)) {
getCopyStatus('Successfully copy parameters to clipboard in form of python dict !', 'success'); getCopyStatus('Successfully copy parameters to clipboard in form of python dict !', 'success');
} else { } else {
......
...@@ -48,6 +48,7 @@ class TrialsDetail extends React.Component<{}, TrialDetailState> { ...@@ -48,6 +48,7 @@ class TrialsDetail extends React.Component<{}, TrialDetailState> {
render(): React.ReactNode { render(): React.ReactNode {
const { whichChart } = this.state; const { whichChart } = this.state;
const source = TRIALS.toArray(); const source = TRIALS.toArray();
const paraSource = TRIALS.succeededTrials();
const succeededTrialIds = TRIALS.succeededTrials().map(trial => trial.id); const succeededTrialIds = TRIALS.succeededTrials().map(trial => trial.id);
return ( return (
<AppContext.Consumer> <AppContext.Consumer>
...@@ -74,12 +75,12 @@ class TrialsDetail extends React.Component<{}, TrialDetailState> { ...@@ -74,12 +75,12 @@ class TrialsDetail extends React.Component<{}, TrialDetailState> {
{/* <PivotItem tab={this.titleOfhyper} key="2"> */} {/* <PivotItem tab={this.titleOfhyper} key="2"> */}
<PivotItem headerText='Hyper-parameter' itemIcon='Equalizer' key='Hyper-parameter'> <PivotItem headerText='Hyper-parameter' itemIcon='Equalizer' key='Hyper-parameter'>
<Stack className='graph'> <Stack className='graph'>
<Para trials={source} searchSpace={EXPERIMENT.searchSpaceNew} /> <Para trials={paraSource} searchSpace={EXPERIMENT.searchSpaceNew} />
</Stack> </Stack>
</PivotItem> </PivotItem>
{/* <PivotItem tab={this.titleOfDuration} key="3"> */} {/* <PivotItem tab={this.titleOfDuration} key="3"> */}
<PivotItem headerText='Duration' itemIcon='BarChartHorizontal' key='Duration'> <PivotItem headerText='Duration' itemIcon='BarChartHorizontal' key='Duration'>
<Duration source={source} /> <Duration source={TRIALS.notWaittingTrials()} />
</PivotItem> </PivotItem>
{/* <PivotItem tab={this.titleOfIntermediate} key="4"> */} {/* <PivotItem tab={this.titleOfIntermediate} key="4"> */}
<PivotItem <PivotItem
...@@ -88,7 +89,7 @@ class TrialsDetail extends React.Component<{}, TrialDetailState> { ...@@ -88,7 +89,7 @@ class TrialsDetail extends React.Component<{}, TrialDetailState> {
key='Intermediate result' key='Intermediate result'
> >
{/* *why this graph has small footprint? */} {/* *why this graph has small footprint? */}
<Intermediate source={source} /> <Intermediate source={TRIALS.allTrialsIntermediateChart()} />
</PivotItem> </PivotItem>
</Pivot> </Pivot>
</div> </div>
......
...@@ -138,16 +138,17 @@ class DefaultPoint extends React.Component<DefaultPointProps, DefaultPointState> ...@@ -138,16 +138,17 @@ class DefaultPoint extends React.Component<DefaultPointProps, DefaultPointState>
trial.sequenceId, trial.sequenceId,
trial.acc === undefined ? 0 : this.formatAccuracy(trial.acc[userSelectAccuracyNumberKey]), trial.acc === undefined ? 0 : this.formatAccuracy(trial.acc[userSelectAccuracyNumberKey]),
trial.id, trial.id,
trial.description.parameters trial.parameter
]); ]);
} else { } else {
data = trials.map(trial => [ data = trials.map(trial => [
trial.sequenceId, trial.sequenceId,
this.formatAccuracy(trial.accuracy), this.formatAccuracy(trial.accuracy),
trial.id, trial.id,
trial.description.parameters trial.parameter
]); ]);
} }
return { return {
symbolSize: 6, symbolSize: 6,
type: 'scatter', type: 'scatter',
...@@ -163,7 +164,7 @@ class DefaultPoint extends React.Component<DefaultPointProps, DefaultPointState> ...@@ -163,7 +164,7 @@ class DefaultPoint extends React.Component<DefaultPointProps, DefaultPointState>
best.sequenceId, best.sequenceId,
best.acc === undefined ? 0 : this.formatAccuracy(best.acc[userSelectAccuracyNumberKey]), best.acc === undefined ? 0 : this.formatAccuracy(best.acc[userSelectAccuracyNumberKey]),
best.id, best.id,
best.description.parameters best.parameter
] ]
]; ];
for (let i = 1; i < trials.length; i++) { for (let i = 1; i < trials.length; i++) {
...@@ -176,7 +177,7 @@ class DefaultPoint extends React.Component<DefaultPointProps, DefaultPointState> ...@@ -176,7 +177,7 @@ class DefaultPoint extends React.Component<DefaultPointProps, DefaultPointState>
trial.sequenceId, trial.sequenceId,
trial.acc === undefined ? 0 : this.formatAccuracy(trial.acc[userSelectAccuracyNumberKey]), trial.acc === undefined ? 0 : this.formatAccuracy(trial.acc[userSelectAccuracyNumberKey]),
best.id, best.id,
trial.description.parameters trial.parameter
]); ]);
best = trial; best = trial;
} else { } else {
...@@ -184,7 +185,7 @@ class DefaultPoint extends React.Component<DefaultPointProps, DefaultPointState> ...@@ -184,7 +185,7 @@ class DefaultPoint extends React.Component<DefaultPointProps, DefaultPointState>
trial.sequenceId, trial.sequenceId,
best.acc === undefined ? 0 : this.formatAccuracy(best.acc[userSelectAccuracyNumberKey]), best.acc === undefined ? 0 : this.formatAccuracy(best.acc[userSelectAccuracyNumberKey]),
best.id, best.id,
trial.description.parameters trial.parameter
]); ]);
} }
} }
......
import * as React from 'react'; import * as React from 'react';
import ReactEcharts from 'echarts-for-react'; import ReactEcharts from 'echarts-for-react';
import { TableObj, EventMap } from '@static/interface'; import { EventMap } from '@static/interface';
import { filterDuration, convertDuration } from '@static/function'; import { Trial } from '@model/trial';
import { convertDuration } from '@static/function';
import 'echarts/lib/chart/bar'; import 'echarts/lib/chart/bar';
import 'echarts/lib/component/tooltip'; import 'echarts/lib/component/tooltip';
import 'echarts/lib/component/title'; import 'echarts/lib/component/title';
interface Runtrial { interface Runtrial {
trialId: string[]; trialId: number[];
trialTime: number[]; trialTime: number[];
} }
interface DurationProps { interface DurationProps {
source: Array<TableObj>; source: Trial[];
} }
interface DurationState { interface DurationState {
...@@ -31,12 +32,10 @@ class Duration extends React.Component<DurationProps, DurationState> { ...@@ -31,12 +32,10 @@ class Duration extends React.Component<DurationProps, DurationState> {
}; };
} }
initDuration = (source: Array<TableObj>): any => { initDuration = (source: Trial[]): any => {
const trialId: number[] = []; const trialId: number[] = [];
const trialTime: number[] = []; const trialTime: number[] = [];
const trialJobs = source.filter(filterDuration); source.forEach(item => {
trialJobs.forEach(item => {
trialId.push(item.sequenceId); trialId.push(item.sequenceId);
trialTime.push(item.duration); trialTime.push(item.duration);
}); });
...@@ -146,17 +145,16 @@ class Duration extends React.Component<DurationProps, DurationState> { ...@@ -146,17 +145,16 @@ class Duration extends React.Component<DurationProps, DurationState> {
}; };
}; };
drawDurationGraph = (source: Array<TableObj>): void => { drawDurationGraph = (source: Trial[]): void => {
// why this function run two times when props changed? // why this function run two times when props changed?
const trialId: string[] = []; const trialId: number[] = [];
const trialTime: number[] = []; const trialTime: number[] = [];
const trialRun: Runtrial[] = []; const trialRun: Runtrial[] = [];
const trialJobs = source.filter(filterDuration); source.forEach(item => {
Object.keys(trialJobs).map(item => { trialId.push(item.sequenceId);
const temp = trialJobs[item]; trialTime.push(item.duration);
trialId.push(temp.sequenceId);
trialTime.push(temp.duration);
}); });
trialRun.push({ trialRun.push({
trialId: trialId, trialId: trialId,
trialTime: trialTime trialTime: trialTime
......
import * as React from 'react'; import * as React from 'react';
import { Stack, PrimaryButton, Toggle, IStackTokens } from '@fluentui/react'; import { Stack, PrimaryButton, Toggle, IStackTokens } from '@fluentui/react';
import { TooltipForIntermediate, TableObj, Intermedia, EventMap } from '@static/interface'; import { TooltipForIntermediate, EventMap, allTrialsIntermediateChart } from '@static/interface';
import { reformatRetiariiParameter } from '@static/function'; import { reformatRetiariiParameter } from '@static/function';
import ReactEcharts from 'echarts-for-react'; import ReactEcharts from 'echarts-for-react';
import 'echarts/lib/component/tooltip'; import 'echarts/lib/component/tooltip';
...@@ -11,20 +11,19 @@ const stackTokens: IStackTokens = { ...@@ -11,20 +11,19 @@ const stackTokens: IStackTokens = {
}; };
interface IntermediateState { interface IntermediateState {
detailSource: Array<TableObj>; detailSource: allTrialsIntermediateChart[];
interSource: object; interSource: object;
filterSource: Array<TableObj>; filterSource: allTrialsIntermediateChart[];
eachIntermediateNum: number; // trial's intermediate number count eachIntermediateNum: number; // trial's intermediate number count
isLoadconfirmBtn: boolean; isLoadconfirmBtn: boolean;
isFilter?: boolean | undefined; isFilter?: boolean | undefined;
length: number; length: number;
clickCounts: number; // user filter intermediate click confirm btn's counts
startMediaY: number; startMediaY: number;
endMediaY: number; endMediaY: number;
} }
interface IntermediateProps { interface IntermediateProps {
source: Array<TableObj>; source: allTrialsIntermediateChart[];
} }
class Intermediate extends React.Component<IntermediateProps, IntermediateState> { class Intermediate extends React.Component<IntermediateProps, IntermediateState> {
...@@ -43,43 +42,24 @@ class Intermediate extends React.Component<IntermediateProps, IntermediateState> ...@@ -43,43 +42,24 @@ class Intermediate extends React.Component<IntermediateProps, IntermediateState>
isLoadconfirmBtn: false, isLoadconfirmBtn: false,
isFilter: false, isFilter: false,
length: 100000, length: 100000,
clickCounts: 0,
startMediaY: 0, startMediaY: 0,
endMediaY: 100 endMediaY: 100
}; };
} }
drawIntermediate = (source: Array<TableObj>): void => { drawIntermediate = (source: allTrialsIntermediateChart[]): void => {
if (source.length > 0) { if (source.length > 0) {
this.setState({ this.setState({
length: source.length, length: source.length,
detailSource: source detailSource: source
}); });
const { startMediaY, endMediaY } = this.state; const { startMediaY, endMediaY } = this.state;
const trialIntermediate: Array<Intermedia> = [];
Object.keys(source).map(item => {
const temp = source[item];
trialIntermediate.push({
name: temp.id,
trialNum: temp.sequenceId,
data: temp.description.intermediate,
type: 'line',
hyperPara: temp.description.parameters
});
});
// find max intermediate number
trialIntermediate.sort((a, b) => {
return b.data.length - a.data.length;
});
const legend: string[] = [];
// max length
const length = trialIntermediate[0].data.length;
const xAxis: number[] = []; const xAxis: number[] = [];
Object.keys(trialIntermediate).map(item => { // find having most intermediate number
const temp = trialIntermediate[item]; source.sort((a, b) => {
legend.push(temp.name); return b.data.length - a.data.length;
}); });
for (let i = 1; i <= length; i++) { for (let i = 1; i <= source[0].data.length; i++) {
xAxis.push(i); xAxis.push(i);
} }
const option = { const option = {
...@@ -89,20 +69,21 @@ class Intermediate extends React.Component<IntermediateProps, IntermediateState> ...@@ -89,20 +69,21 @@ class Intermediate extends React.Component<IntermediateProps, IntermediateState>
confine: true, confine: true,
formatter: function (data: TooltipForIntermediate): React.ReactNode { formatter: function (data: TooltipForIntermediate): React.ReactNode {
const trialId = data.seriesName; const trialId = data.seriesName;
let parameters = {}; // parameter and trialNo need to have the init value otherwise maybe cause page broke down
let trialNum = 0; let parameter = {};
const temp = trialIntermediate.find(key => key.name === trialId); let trialNo = 0;
if (temp !== undefined) { const renderTrial = source.find(key => key.name === trialId);
parameters = temp.hyperPara; if (renderTrial !== undefined) {
trialNum = temp.trialNum; parameter = renderTrial.parameter;
trialNo = renderTrial.sequenceId;
} }
return ` return `
<div class="tooldetailAccuracy"> <div class="tooldetailAccuracy">
<div>Trial No.: ${trialNum}</div> <div>Trial No.: ${trialNo}</div>
<div>Trial ID: ${trialId}</div> <div>Trial ID: ${trialId}</div>
<div>Intermediate: ${data.data}</div> <div>Intermediate: ${data.data}</div>
<div>Parameters: <pre>${JSON.stringify( <div>Parameters: <pre>${JSON.stringify(
reformatRetiariiParameter(parameters), reformatRetiariiParameter(parameter),
null, null,
4 4
)}</pre> )}</pre>
...@@ -137,7 +118,7 @@ class Intermediate extends React.Component<IntermediateProps, IntermediateState> ...@@ -137,7 +118,7 @@ class Intermediate extends React.Component<IntermediateProps, IntermediateState>
end: endMediaY end: endMediaY
} }
], ],
series: trialIntermediate series: source
}; };
this.setState({ this.setState({
interSource: option interSource: option
...@@ -164,7 +145,7 @@ class Intermediate extends React.Component<IntermediateProps, IntermediateState> ...@@ -164,7 +145,7 @@ class Intermediate extends React.Component<IntermediateProps, IntermediateState>
// confirm btn function [filter data] // confirm btn function [filter data]
filterLines = (): void => { filterLines = (): void => {
const filterSource: Array<TableObj> = []; const filterSource: allTrialsIntermediateChart[] = [];
this.setState({ isLoadconfirmBtn: true }, () => { this.setState({ isLoadconfirmBtn: true }, () => {
const { source } = this.props; const { source } = this.props;
// get input value // get input value
...@@ -175,32 +156,29 @@ class Intermediate extends React.Component<IntermediateProps, IntermediateState> ...@@ -175,32 +156,29 @@ class Intermediate extends React.Component<IntermediateProps, IntermediateState>
if (pointVal === '' || minVal === '') { if (pointVal === '' || minVal === '') {
alert('Please input filter message'); alert('Please input filter message');
} else { } else {
// user not input max value
const position = JSON.parse(pointVal); const position = JSON.parse(pointVal);
const min = JSON.parse(minVal); const min = JSON.parse(minVal);
if (maxVal === '') { if (maxVal === '') {
Object.keys(source).map(item => { // user not input max value
const temp = source[item]; for (const item of source) {
const val = temp.description.intermediate[position - 1]; const val = item.data[position - 1];
if (val >= min) { if (val >= min) {
filterSource.push(temp); filterSource.push(item);
} }
}); }
} else { } else {
const max = JSON.parse(maxVal); const max = JSON.parse(maxVal);
Object.keys(source).map(item => { for (const item of source) {
const temp = source[item]; const val = item.data[position - 1];
const val = temp.description.intermediate[position - 1];
if (val >= min && val <= max) { if (val >= min && val <= max) {
filterSource.push(temp); filterSource.push(item);
} }
}); }
} }
this.setState({ filterSource: filterSource }); this.setState({ filterSource: filterSource });
this.drawIntermediate(filterSource); this.drawIntermediate(filterSource);
} }
const counts = this.state.clickCounts + 1; this.setState({ isLoadconfirmBtn: false });
this.setState({ isLoadconfirmBtn: false, clickCounts: counts });
}); });
}; };
......
...@@ -3,9 +3,9 @@ import * as d3 from 'd3'; ...@@ -3,9 +3,9 @@ import * as d3 from 'd3';
import { Dropdown, IDropdownOption, Stack, DefaultButton } from '@fluentui/react'; import { Dropdown, IDropdownOption, Stack, DefaultButton } from '@fluentui/react';
import ParCoords from 'parcoord-es'; import ParCoords from 'parcoord-es';
import { SearchSpace } from '@model/searchspace'; import { SearchSpace } from '@model/searchspace';
import { filterByStatus } from '@static/function';
import { EXPERIMENT, TRIALS } from '@static/datamodel'; import { EXPERIMENT, TRIALS } from '@static/datamodel';
import { TableObj, SingleAxis, MultipleAxes } from '@static/interface'; import { SingleAxis, MultipleAxes } from '@static/interface';
import { Trial } from '@model/trial';
import ChangeColumnComponent from '../ChangeColumnComponent'; import ChangeColumnComponent from '../ChangeColumnComponent';
import { optimizeModeValue } from './optimizeMode'; import { optimizeModeValue } from './optimizeMode';
...@@ -25,7 +25,7 @@ interface ParaState { ...@@ -25,7 +25,7 @@ interface ParaState {
} }
interface ParaProps { interface ParaProps {
trials: Array<TableObj>; trials: Trial[];
searchSpace: SearchSpace; searchSpace: SearchSpace;
} }
...@@ -304,9 +304,8 @@ class Para extends React.Component<ParaProps, ParaState> { ...@@ -304,9 +304,8 @@ class Para extends React.Component<ParaProps, ParaState> {
private getTrialsAsObjectList(inferredSearchSpace: MultipleAxes, inferredMetricSpace: MultipleAxes): {}[] { private getTrialsAsObjectList(inferredSearchSpace: MultipleAxes, inferredMetricSpace: MultipleAxes): {}[] {
const { trials } = this.props; const { trials } = this.props;
const succeededTrials = trials.filter(filterByStatus);
return succeededTrials.map(s => { return trials.map(s => {
const entries = Array.from(s.parameters(inferredSearchSpace).entries()); const entries = Array.from(s.parameters(inferredSearchSpace).entries());
entries.push(...Array.from(s.metrics(inferredMetricSpace).entries())); entries.push(...Array.from(s.metrics(inferredMetricSpace).entries()));
const ret = {}; const ret = {};
......
...@@ -13,22 +13,15 @@ import { ...@@ -13,22 +13,15 @@ import {
import { Trial } from '@model/trial'; import { Trial } from '@model/trial';
import { TOOLTIP_BACKGROUND_COLOR } from '@static/const'; import { TOOLTIP_BACKGROUND_COLOR } from '@static/const';
import { EXPERIMENT, TRIALS } from '@static/datamodel'; import { EXPERIMENT, TRIALS } from '@static/datamodel';
import { import { convertDuration, formatTimestamp, copyAndSort, parametersType, _inferColumnTitle } from '@static/function';
convertDuration, import { SortInfo, SearchItems } from '@static/interface';
formatTimestamp,
copyAndSort,
parametersType,
_inferColumnTitle,
getIntermediateAllKeys
} from '@static/function';
import { TableObj, SortInfo, SearchItems } from '@static/interface';
import { blocked, copy, LineChart, tableListIcon } from '@components/fluent/Icon'; import { blocked, copy, LineChart, tableListIcon } from '@components/fluent/Icon';
import Customize from './tableFunction/CustomizedTrial'; import Customize from './tableFunction/CustomizedTrial';
import TensorboardUI from './tableFunction/tensorboard/TensorboardUI'; import TensorboardUI from './tableFunction/tensorboard/TensorboardUI';
import Search from './tableFunction/search/Search'; import Search from './tableFunction/search/Search';
import ExpandableDetails from '@components/common/ExpandableDetails/ExpandableIndex'; import ExpandableDetails from '@components/common/ExpandableDetails/ExpandableIndex';
import ChangeColumnComponent from '../ChangeColumnComponent'; import ChangeColumnComponent from '../ChangeColumnComponent';
import Compare from './tableFunction/Compare'; import Compare from './tableFunction/CompareIndex';
import KillJobIndex from './tableFunction/killJob/KillJobIndex'; import KillJobIndex from './tableFunction/killJob/KillJobIndex';
import { getTrialsBySearchFilters } from './tableFunction/search/searchFunction'; import { getTrialsBySearchFilters } from './tableFunction/search/searchFunction';
import PaginationTable from '@components/common/PaginationTable'; import PaginationTable from '@components/common/PaginationTable';
...@@ -43,7 +36,7 @@ type SearchOptionType = 'id' | 'trialnum' | 'status' | 'parameters'; ...@@ -43,7 +36,7 @@ type SearchOptionType = 'id' | 'trialnum' | 'status' | 'parameters';
const defaultDisplayedColumns = ['sequenceId', 'id', 'duration', 'status', 'latestAccuracy']; const defaultDisplayedColumns = ['sequenceId', 'id', 'duration', 'status', 'latestAccuracy'];
interface TableListProps { interface TableListProps {
tableSource: TableObj[]; tableSource: Trial[];
} }
interface TableListState { interface TableListState {
...@@ -55,12 +48,11 @@ interface TableListState { ...@@ -55,12 +48,11 @@ interface TableListState {
selectedRowIds: string[]; selectedRowIds: string[];
customizeColumnsDialogVisible: boolean; customizeColumnsDialogVisible: boolean;
compareDialogVisible: boolean; compareDialogVisible: boolean;
intermediateDialogTrial: TableObj | undefined; intermediateDialogTrial: Trial[] | undefined;
copiedTrialId: string | undefined; copiedTrialId: string | undefined;
sortInfo: SortInfo; sortInfo: SortInfo;
searchItems: Array<SearchItems>; searchItems: Array<SearchItems>;
relation: Map<string, string>; relation: Map<string, string>;
intermediateKeyList: string[];
} }
class TableList extends React.Component<TableListProps, TableListState> { class TableList extends React.Component<TableListProps, TableListState> {
...@@ -86,8 +78,7 @@ class TableList extends React.Component<TableListProps, TableListState> { ...@@ -86,8 +78,7 @@ class TableList extends React.Component<TableListProps, TableListState> {
copiedTrialId: undefined, copiedTrialId: undefined,
sortInfo: { field: '', isDescend: true }, sortInfo: { field: '', isDescend: true },
searchItems: [], searchItems: [],
relation: parametersType(), relation: parametersType()
intermediateKeyList: []
}; };
this._expandedTrialIds = new Set<string>(); this._expandedTrialIds = new Set<string>();
...@@ -113,8 +104,7 @@ class TableList extends React.Component<TableListProps, TableListState> { ...@@ -113,8 +104,7 @@ class TableList extends React.Component<TableListProps, TableListState> {
selectedRowIds, selectedRowIds,
intermediateDialogTrial, intermediateDialogTrial,
copiedTrialId, copiedTrialId,
searchItems, searchItems
intermediateKeyList
} = this.state; } = this.state;
return ( return (
...@@ -145,10 +135,23 @@ class TableList extends React.Component<TableListProps, TableListState> { ...@@ -145,10 +135,23 @@ class TableList extends React.Component<TableListProps, TableListState> {
text='Compare' text='Compare'
className='allList-compare' className='allList-compare'
onClick={(): void => { onClick={(): void => {
this.setState({ compareDialogVisible: true }); this.setState({
compareDialogVisible: true
});
}} }}
disabled={selectedRowIds.length === 0} disabled={selectedRowIds.length === 0}
/> />
{/* compare model: trial intermediates graph; table: id,no,status,default dict value */}
{compareDialogVisible && (
<Compare
title='Compare trials'
trials={this.props.tableSource.filter(trial => selectedRowIds.includes(trial.id))}
onHideDialog={(): void => {
this.setState({ compareDialogVisible: false });
}}
changeSelectTrialIds={this.changeSelectTrialIds}
/>
)}
<TensorboardUI <TensorboardUI
selectedRowIds={selectedRowIds} selectedRowIds={selectedRowIds}
changeSelectTrialIds={this.changeSelectTrialIds} changeSelectTrialIds={this.changeSelectTrialIds}
...@@ -172,23 +175,10 @@ class TableList extends React.Component<TableListProps, TableListState> { ...@@ -172,23 +175,10 @@ class TableList extends React.Component<TableListProps, TableListState> {
}} }}
/> />
)} )}
{compareDialogVisible && (
<Compare
title='Compare trials'
showDetails={true}
trials={this.props.tableSource.filter(trial => selectedRowIds.includes(trial.id))}
onHideDialog={(): void => {
this.setState({ compareDialogVisible: false });
}}
changeSelectTrialIds={this.changeSelectTrialIds}
/>
)}
{intermediateDialogTrial !== undefined && ( {intermediateDialogTrial !== undefined && (
<Compare <Compare
title='Intermediate results' title='Intermediate results'
showDetails={false} trials={intermediateDialogTrial}
trials={[intermediateDialogTrial]}
intermediateKeyList={intermediateKeyList}
onHideDialog={(): void => { onHideDialog={(): void => {
this.setState({ intermediateDialogTrial: undefined }); this.setState({ intermediateDialogTrial: undefined });
}} }}
...@@ -236,32 +226,21 @@ class TableList extends React.Component<TableListProps, TableListState> { ...@@ -236,32 +226,21 @@ class TableList extends React.Component<TableListProps, TableListState> {
); );
} }
private _trialsToTableItems(trials: TableObj[]): any[] { private _trialsToTableItems(trials: Trial[]): any[] {
// TODO: use search space and metrics space from TRIALS will cause update issues. // TODO: use search space and metrics space from TRIALS will cause update issues.
const searchSpace = TRIALS.inferredSearchSpace(EXPERIMENT.searchSpaceNew); const searchSpace = TRIALS.inferredSearchSpace(EXPERIMENT.searchSpaceNew);
const metricSpace = TRIALS.inferredMetricSpace(); const metricSpace = TRIALS.inferredMetricSpace();
const { selectedRowIds } = this.state; const { selectedRowIds } = this.state;
const items = trials.map(trial => { const items = trials.map(trial => {
const ret = { const ret = trial.tableRecord;
sequenceId: trial.sequenceId, ret['_checked'] = selectedRowIds.includes(trial.id) ? true : false;
id: trial.id, ret['_expandDetails'] = this._expandedTrialIds.has(trial.id); // hidden field names should start with `_`
_checked: selectedRowIds.includes(trial.id) ? true : false,
startTime: (trial as Trial).info.startTime, // FIXME: why do we need info here?
endTime: (trial as Trial).info.endTime,
duration: trial.duration,
status: trial.status,
message: (trial as Trial).info.message || '--',
intermediateCount: trial.intermediates.length,
_expandDetails: this._expandedTrialIds.has(trial.id) // hidden field names should start with `_`
};
for (const [k, v] of trial.parameters(searchSpace)) { for (const [k, v] of trial.parameters(searchSpace)) {
ret[`space/${k.baseName}`] = v; ret[`space/${k.baseName}`] = v;
} }
for (const [k, v] of trial.metrics(metricSpace)) { for (const [k, v] of trial.metrics(metricSpace)) {
ret[`metric/${k.baseName}`] = v; ret[`metric/${k.baseName}`] = v;
} }
ret['latestAccuracy'] = (trial as Trial).latestAccuracy;
ret['_formattedLatestAccuracy'] = (trial as Trial).formatLatestAccuracy();
return ret; return ret;
}); });
...@@ -397,9 +376,6 @@ class TableList extends React.Component<TableListProps, TableListState> { ...@@ -397,9 +376,6 @@ class TableList extends React.Component<TableListProps, TableListState> {
...(k === 'status' && { ...(k === 'status' && {
// color status // color status
onRender: (record): React.ReactNode => ( onRender: (record): React.ReactNode => (
// kill 成功之后,重新拉取的数据如果有 endtime 字段,会马上render出user_cancel
// 的状态,反之,没有这个字段,table依然是部分刷新,只刷新duration,不会
// 刷新 status
<span className={`${record.status} commonStyle`}>{record.status}</span> <span className={`${record.status} commonStyle`}>{record.status}</span>
) )
}), }),
...@@ -461,7 +437,7 @@ class TableList extends React.Component<TableListProps, TableListState> { ...@@ -461,7 +437,7 @@ class TableList extends React.Component<TableListProps, TableListState> {
} }
}} }}
> >
<div className='ellipsis'>{record._formattedLatestAccuracy}</div> <div className='ellipsis'>{record.formattedLatestAccuracy}</div>
</TooltipHost> </TooltipHost>
) )
}), }),
...@@ -545,11 +521,9 @@ class TableList extends React.Component<TableListProps, TableListState> { ...@@ -545,11 +521,9 @@ class TableList extends React.Component<TableListProps, TableListState> {
title='Intermediate' title='Intermediate'
onClick={(): void => { onClick={(): void => {
const { tableSource } = this.props; const { tableSource } = this.props;
const trial = tableSource.find(trial => trial.id === record.id) as TableObj; const trial = tableSource.find(trial => trial.id === record.id) as Trial;
const intermediateKeyListResult = getIntermediateAllKeys(trial);
this.setState({ this.setState({
intermediateDialogTrial: trial, intermediateDialogTrial: [trial]
intermediateKeyList: intermediateKeyListResult
}); });
}} }}
> >
......
import * as React from 'react'; import React, { useState, useEffect } from 'react';
import { renderToString } from 'react-dom/server'; import { renderToString } from 'react-dom/server';
import { Stack, Modal, IconButton, IDragOptions, ContextualMenu, Dropdown, IDropdownOption } from '@fluentui/react'; import { Stack, Modal, IconButton, IDragOptions, ContextualMenu, Dropdown, IDropdownOption } from '@fluentui/react';
import ReactEcharts from 'echarts-for-react'; import ReactEcharts from 'echarts-for-react';
import { TooltipForIntermediate, TableObj, SingleAxis } from '@static/interface';
import { contentStyles, iconButtonStyles } from '@components/fluent/ModalTheme';
import { convertDuration, parseMetrics } from '@static/function';
import { EXPERIMENT, TRIALS } from '@static/datamodel'; import { EXPERIMENT, TRIALS } from '@static/datamodel';
import { Trial } from '@model/trial';
import { TooltipForIntermediate, SingleAxis } from '@static/interface';
import { contentStyles, iconButtonStyles } from '@components/fluent/ModalTheme';
import { convertDuration, parseMetrics, getIntermediateAllKeys } from '@static/function';
import '@style/experiment/trialdetail/compare.scss'; import '@style/experiment/trialdetail/compare.scss';
/*** /***
...@@ -26,7 +27,7 @@ const dragOptions: IDragOptions = { ...@@ -26,7 +27,7 @@ const dragOptions: IDragOptions = {
// TODO: this should be refactored to the common modules // TODO: this should be refactored to the common modules
// copied from trial.ts // copied from trial.ts
function _parseIntermediates(trial: TableObj, key: string): number[] { function _parseIntermediates(trial: Trial, key: string): number[] {
const intermediates: number[] = []; const intermediates: number[] = [];
for (const metric of trial.intermediates) { for (const metric of trial.intermediates) {
if (metric === undefined) { if (metric === undefined) {
...@@ -53,30 +54,46 @@ interface Item { ...@@ -53,30 +54,46 @@ interface Item {
} }
interface CompareProps { interface CompareProps {
trials: TableObj[]; trials: Trial[];
title: string; title: string;
showDetails: boolean;
intermediateKeyList?: string[];
onHideDialog: () => void; onHideDialog: () => void;
changeSelectTrialIds?: () => void; changeSelectTrialIds?: () => void;
} }
interface CompareState { function CompareIndex(props: CompareProps): any {
intermediateKey: string; // default, dict other keys const { trials, title } = props;
} const atrial = trials.find(item => item.intermediates.length > 0);
const intermediateAllKeysList = getIntermediateAllKeys(atrial === undefined ? trials[0] : atrial);
const [intermediateKey, setIntermediateKey] = useState(
intermediateAllKeysList.length > 0 ? intermediateAllKeysList[0] : 'default'
);
const runningTrial = trials.find(item => item.status === 'RUNNING');
const runningTrialIntermediateListLength = runningTrial !== undefined ? runningTrial.intermediates.length : -1;
class Compare extends React.Component<CompareProps, CompareState> { function itemsList(): Item[] {
constructor(props: CompareProps) { const inferredSearchSpace = TRIALS.inferredSearchSpace(EXPERIMENT.searchSpaceNew);
super(props); const flatten = (m: Map<SingleAxis, any>): Map<string, any> => {
return new Map(Array.from(m).map(([key, value]) => [key.baseName, value]));
this.state = {
// intermediate result maybe don't have the 'default' key...
intermediateKey:
this.props.intermediateKeyList !== undefined ? this.props.intermediateKeyList[0] : 'default'
}; };
return trials.map(trial => ({
id: trial.id,
sequenceId: trial.sequenceId,
duration: convertDuration(trial.duration),
parameters: flatten(trial.parameters(inferredSearchSpace)),
metrics: flatten(trial.metrics(TRIALS.inferredMetricSpace())),
intermediates: _parseIntermediates(trial, intermediateKey)
}));
} }
private _generateTooltipSummary = (row: Item, value: string): string => const [items, setItems] = useState(itemsList());
// react componentDidMount & componentDidUpdate
useEffect(() => {
setItems(itemsList());
}, [intermediateKey, runningTrialIntermediateListLength]); // update condition
// page related function
const _generateTooltipSummary = (row: Item, value: string): string =>
renderToString( renderToString(
<div className='tooldetailAccuracy'> <div className='tooldetailAccuracy'>
<div>Trial No.: {row.sequenceId}</div> <div>Trial No.: {row.sequenceId}</div>
...@@ -85,7 +102,7 @@ class Compare extends React.Component<CompareProps, CompareState> { ...@@ -85,7 +102,7 @@ class Compare extends React.Component<CompareProps, CompareState> {
</div> </div>
); );
private _intermediates(items: Item[]): React.ReactNode { function _intermediates(items: Item[]): React.ReactNode {
// Precondition: make sure `items` is not empty // Precondition: make sure `items` is not empty
const xAxisMax = Math.max(...items.map(item => item.intermediates.length)); const xAxisMax = Math.max(...items.map(item => item.intermediates.length));
const xAxis = Array(xAxisMax) const xAxis = Array(xAxisMax)
...@@ -104,7 +121,7 @@ class Compare extends React.Component<CompareProps, CompareState> { ...@@ -104,7 +121,7 @@ class Compare extends React.Component<CompareProps, CompareState> {
confine: true, confine: true,
formatter: (data: TooltipForIntermediate): string => { formatter: (data: TooltipForIntermediate): string => {
const item = items.find(k => k.id === data.seriesName) as Item; const item = items.find(k => k.id === data.seriesName) as Item;
return this._generateTooltipSummary(item, data.data); return _generateTooltipSummary(item, data.data);
} }
}, },
grid: { grid: {
...@@ -141,7 +158,7 @@ class Compare extends React.Component<CompareProps, CompareState> { ...@@ -141,7 +158,7 @@ class Compare extends React.Component<CompareProps, CompareState> {
); );
} }
private _renderRow( function _renderRow(
key: string, key: string,
rowName: string, rowName: string,
className: string, className: string,
...@@ -160,7 +177,7 @@ class Compare extends React.Component<CompareProps, CompareState> { ...@@ -160,7 +177,7 @@ class Compare extends React.Component<CompareProps, CompareState> {
); );
} }
private _overlapKeys(s: Map<string, any>[]): string[] { function _overlapKeys(s: Map<string, any>[]): string[] {
// Calculate the overlapped keys for multiple // Calculate the overlapped keys for multiple
const intersection: string[] = []; const intersection: string[] = [];
for (const i of s[0].keys()) { for (const i of s[0].keys()) {
...@@ -179,7 +196,7 @@ class Compare extends React.Component<CompareProps, CompareState> { ...@@ -179,7 +196,7 @@ class Compare extends React.Component<CompareProps, CompareState> {
} }
// render table column --- // render table column ---
private _columns(items: Item[]): React.ReactNode { function _columns(items: Item[]): React.ReactNode {
// Precondition: make sure `items` is not empty // Precondition: make sure `items` is not empty
const width = _getWebUIWidth(); const width = _getWebUIWidth();
let scrollClass: string = ''; let scrollClass: string = '';
...@@ -190,23 +207,21 @@ class Compare extends React.Component<CompareProps, CompareState> { ...@@ -190,23 +207,21 @@ class Compare extends React.Component<CompareProps, CompareState> {
} else { } else {
scrollClass = items.length > 2 ? 'flex' : ''; scrollClass = items.length > 2 ? 'flex' : '';
} }
const parameterKeys = this._overlapKeys(items.map(item => item.parameters)); const parameterKeys = _overlapKeys(items.map(item => item.parameters));
const metricKeys = this._overlapKeys(items.map(item => item.metrics)); const metricKeys = _overlapKeys(items.map(item => item.metrics));
return ( return (
<table className={`compare-modal-table ${scrollClass} fontColor333`}> <table className={`compare-modal-table ${scrollClass} fontColor333`}>
<tbody> <tbody>
{this._renderRow('id', 'ID', 'value idList', items, item => item.id)} {_renderRow('id', 'ID', 'value idList', items, item => item.id)}
{this._renderRow('trialnum', 'Trial No.', 'value', items, item => item.sequenceId.toString())} {_renderRow('trialnum', 'Trial No.', 'value', items, item => item.sequenceId.toString())}
{this._renderRow('duration', 'Duration', 'value', items, item => item.duration)} {_renderRow('duration', 'Duration', 'value', items, item => item.duration)}
{parameterKeys.map(k => {parameterKeys.map(k =>
this._renderRow(`space_${k}`, k, 'value', items, item => item.parameters.get(k)) _renderRow(`space_${k}`, k, 'value', items, item => item.parameters.get(k))
)} )}
{metricKeys !== undefined {metricKeys !== undefined
? metricKeys.map(k => ? metricKeys.map(k =>
this._renderRow(`metrics_${k}`, `Metric: ${k}`, 'value', items, item => _renderRow(`metrics_${k}`, `Metric: ${k}`, 'value', items, item => item.metrics.get(k))
item.metrics.get(k)
)
) )
: null} : null}
</tbody> </tbody>
...@@ -214,80 +229,62 @@ class Compare extends React.Component<CompareProps, CompareState> { ...@@ -214,80 +229,62 @@ class Compare extends React.Component<CompareProps, CompareState> {
); );
} }
private closeCompareModal = (): void => { const closeCompareModal = (): void => {
const { showDetails, changeSelectTrialIds, onHideDialog } = this.props; const { title, changeSelectTrialIds, onHideDialog } = props;
if (showDetails === true) { if (title === 'Compare trials') {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
changeSelectTrialIds!(); changeSelectTrialIds!();
} }
onHideDialog(); onHideDialog();
}; };
private selectOtherKeys = (_event: React.FormEvent<HTMLDivElement>, item?: IDropdownOption): void => { const selectOtherKeys = (_event: React.FormEvent<HTMLDivElement>, item?: IDropdownOption): void => {
if (item !== undefined) { if (item !== undefined) {
this.setState(() => ({ intermediateKey: item.text })); setIntermediateKey(item.text);
} }
}; };
render(): React.ReactNode { return (
const { trials, title, showDetails, intermediateKeyList } = this.props; <Modal
const { intermediateKey } = this.state; isOpen={true}
const intermediateAllKeysList: string[] = intermediateKeyList !== undefined ? intermediateKeyList : []; containerClassName={contentStyles.container}
const flatten = (m: Map<SingleAxis, any>): Map<string, any> => { className='compare-modal'
return new Map(Array.from(m).map(([key, value]) => [key.baseName, value])); allowTouchBodyScroll={true}
}; dragOptions={dragOptions}
const inferredSearchSpace = TRIALS.inferredSearchSpace(EXPERIMENT.searchSpaceNew); onDismiss={closeCompareModal}
const items: Item[] = trials.map(trial => ({ >
id: trial.id, <div>
sequenceId: trial.sequenceId, <div className={contentStyles.header}>
duration: convertDuration(trial.duration), <span>{title}</span>
parameters: flatten(trial.parameters(inferredSearchSpace)), <IconButton
metrics: flatten(trial.metrics(TRIALS.inferredMetricSpace())), styles={iconButtonStyles}
intermediates: _parseIntermediates(trial, intermediateKey) iconProps={{ iconName: 'Cancel' }}
})); ariaLabel='Close popup modal'
onClick={closeCompareModal}
return ( />
<Modal </div>
isOpen={true} {intermediateAllKeysList.length > 1 ||
containerClassName={contentStyles.container} (intermediateAllKeysList.length === 1 && intermediateAllKeysList !== ['default']) ? (
className='compare-modal' <Stack horizontalAlign='end' className='selectKeys'>
allowTouchBodyScroll={true} <Dropdown
dragOptions={dragOptions} className='select'
onDismiss={this.closeCompareModal} selectedKey={intermediateKey}
> options={intermediateAllKeysList.map((key, item) => ({
<div> key: key,
<div className={contentStyles.header}> text: intermediateAllKeysList[item]
<span>{title}</span> }))}
<IconButton onChange={selectOtherKeys}
styles={iconButtonStyles}
iconProps={{ iconName: 'Cancel' }}
ariaLabel='Close popup modal'
onClick={this.closeCompareModal}
/> />
</div>
{intermediateAllKeysList.length > 1 ||
(intermediateAllKeysList.length === 1 && intermediateAllKeysList !== ['default']) ? (
<Stack horizontalAlign='end' className='selectKeys'>
<Dropdown
className='select'
selectedKey={intermediateKey}
options={intermediateAllKeysList.map((key, item) => ({
key: key,
text: intermediateAllKeysList[item]
}))}
onChange={this.selectOtherKeys}
/>
</Stack>
) : null}
<Stack className='compare-modal-intermediate'>
{this._intermediates(items)}
<Stack className='compare-yAxis fontColor333'># Intermediate result</Stack>
</Stack> </Stack>
{showDetails && <Stack>{this._columns(items)}</Stack>} ) : null}
</div> <Stack className='compare-modal-intermediate'>
</Modal> {_intermediates(items)}
); <Stack className='compare-yAxis fontColor333'># Intermediate result</Stack>
} </Stack>
{title === 'Compare trials' && <Stack>{_columns(items)}</Stack>}
</div>
</Modal>
);
} }
export default Compare; export default CompareIndex;
...@@ -154,7 +154,7 @@ class Customize extends React.Component<CustomizeProps, CustomizeState> { ...@@ -154,7 +154,7 @@ class Customize extends React.Component<CustomizeProps, CustomizeState> {
componentDidMount(): void { componentDidMount(): void {
const { copyTrialId } = this.props; const { copyTrialId } = this.props;
if (copyTrialId !== undefined && TRIALS.getTrial(copyTrialId) !== undefined) { if (copyTrialId !== undefined && TRIALS.getTrial(copyTrialId) !== undefined) {
const originCopyTrialPara = TRIALS.getTrial(copyTrialId).description.parameters; const originCopyTrialPara = TRIALS.getTrial(copyTrialId).parameter;
this.setState(() => ({ copyTrialParameter: originCopyTrialPara })); this.setState(() => ({ copyTrialParameter: originCopyTrialPara }));
} }
} }
...@@ -163,7 +163,7 @@ class Customize extends React.Component<CustomizeProps, CustomizeState> { ...@@ -163,7 +163,7 @@ class Customize extends React.Component<CustomizeProps, CustomizeState> {
if (this.props.copyTrialId !== prevProps.copyTrialId) { if (this.props.copyTrialId !== prevProps.copyTrialId) {
const { copyTrialId } = this.props; const { copyTrialId } = this.props;
if (copyTrialId !== undefined && TRIALS.getTrial(copyTrialId) !== undefined) { if (copyTrialId !== undefined && TRIALS.getTrial(copyTrialId) !== undefined) {
const originCopyTrialPara = TRIALS.getTrial(copyTrialId).description.parameters; const originCopyTrialPara = TRIALS.getTrial(copyTrialId).parameter;
this.setState(() => ({ copyTrialParameter: originCopyTrialPara })); this.setState(() => ({ copyTrialParameter: originCopyTrialPara }));
} }
} }
......
...@@ -14,7 +14,7 @@ const METRIC_GROUP_UPDATE_SIZE = 20; ...@@ -14,7 +14,7 @@ const METRIC_GROUP_UPDATE_SIZE = 20;
const prefix = getPrefix(); const prefix = getPrefix();
const RESTAPI = '/api/v1/nni'; const RESTAPI = '/api/v1/nni';
const MANAGER_IP = prefix === undefined ? RESTAPI : `${prefix}${RESTAPI}`; const MANAGER_IP = prefix === undefined ? RESTAPI : `${prefix}${RESTAPI}`;
const DOWNLOAD_IP = `${prefix}/logs`; const DOWNLOAD_IP = prefix === undefined ? '/logs' : `${prefix}/logs`;
const WEBUIDOC = 'https://nni.readthedocs.io/en/latest/experiment/webui.html'; const WEBUIDOC = 'https://nni.readthedocs.io/en/latest/experiment/webui.html';
......
...@@ -3,7 +3,7 @@ import axios from 'axios'; ...@@ -3,7 +3,7 @@ import axios from 'axios';
import { IContextualMenuProps } from '@fluentui/react'; import { IContextualMenuProps } from '@fluentui/react';
import { RETIARIIPARAMETERS } from './const'; import { RETIARIIPARAMETERS } from './const';
import { EXPERIMENT } from './datamodel'; import { EXPERIMENT } from './datamodel';
import { MetricDataRecord, FinalType, TableObj, Tensorboard } from './interface'; import { MetricDataRecord, FinalType, Tensorboard } from './interface';
function getPrefix(): string | undefined { function getPrefix(): string | undefined {
const pathName = window.location.pathname; const pathName = window.location.pathname;
...@@ -188,15 +188,6 @@ const intermediateGraphOption = (intermediateArr: number[], id: string): any => ...@@ -188,15 +188,6 @@ const intermediateGraphOption = (intermediateArr: number[], id: string): any =>
}; };
}; };
const filterByStatus = (item: TableObj): boolean => {
return item.status === 'SUCCEEDED';
};
// a waittiong trial may havn't start time
const filterDuration = (item: TableObj): boolean => {
return item.status !== 'WAITING';
};
const downFile = (content: string, fileName: string): void => { const downFile = (content: string, fileName: string): void => {
const aTag = document.createElement('a'); const aTag = document.createElement('a');
const isEdge = navigator.userAgent.indexOf('Edge') !== -1 ? true : false; const isEdge = navigator.userAgent.indexOf('Edge') !== -1 ? true : false;
...@@ -376,8 +367,8 @@ function _inferColumnTitle(columnKey: string): string { ...@@ -376,8 +367,8 @@ function _inferColumnTitle(columnKey: string): string {
const getIntermediateAllKeys = (intermediateDialogTrial: any): string[] => { const getIntermediateAllKeys = (intermediateDialogTrial: any): string[] => {
let intermediateAllKeysList: string[] = []; let intermediateAllKeysList: string[] = [];
if (intermediateDialogTrial!.intermediateMetrics !== undefined && intermediateDialogTrial!.intermediateMetrics[0]) { if (intermediateDialogTrial!.intermediates !== undefined && intermediateDialogTrial!.intermediates[0]) {
const parsedMetric = parseMetrics(intermediateDialogTrial!.intermediateMetrics[0].data); const parsedMetric = parseMetrics(intermediateDialogTrial!.intermediates[0].data);
if (parsedMetric !== undefined && typeof parsedMetric === 'object') { if (parsedMetric !== undefined && typeof parsedMetric === 'object') {
const allIntermediateKeys: string[] = []; const allIntermediateKeys: string[] = [];
// just add type=number keys // just add type=number keys
...@@ -407,8 +398,6 @@ export { ...@@ -407,8 +398,6 @@ export {
getFinal, getFinal,
downFile, downFile,
intermediateGraphOption, intermediateGraphOption,
filterByStatus,
filterDuration,
formatAccuracy, formatAccuracy,
formatTimestamp, formatTimestamp,
expformatTimestamp, expformatTimestamp,
......
...@@ -26,23 +26,6 @@ interface MultipleAxes { ...@@ -26,23 +26,6 @@ interface MultipleAxes {
axes: Map<string, SingleAxis>; axes: Map<string, SingleAxis>;
} }
// draw accuracy graph data export interface
interface TableObj {
key: number;
sequenceId: number;
id: string;
duration: number;
status: string;
acc?: FinalType; // draw accuracy graph
description: Parameters;
color?: string;
startTime?: number;
endTime?: number;
intermediates: (MetricDataRecord | undefined)[];
parameters(axes: MultipleAxes): Map<SingleAxis, any>;
metrics(axes: MultipleAxes): Map<SingleAxis, any>;
}
interface TableRecord { interface TableRecord {
key: string; key: string;
sequenceId: number; sequenceId: number;
...@@ -53,7 +36,6 @@ interface TableRecord { ...@@ -53,7 +36,6 @@ interface TableRecord {
status: string; status: string;
message: string; message: string;
intermediateCount: number; intermediateCount: number;
accuracy?: number | any;
latestAccuracy: number | undefined; latestAccuracy: number | undefined;
formattedLatestAccuracy: string; // format (LATEST/FINAL), formattedLatestAccuracy: string; // format (LATEST/FINAL),
} }
...@@ -67,17 +49,6 @@ interface FinalType { ...@@ -67,17 +49,6 @@ interface FinalType {
default: string; default: string;
} }
interface ErrorParameter {
error?: string;
}
interface Parameters {
parameters: ErrorParameter;
logPath?: string;
intermediate: number[];
multiProgress?: number;
}
// trial accuracy // trial accuracy
interface AccurPoint { interface AccurPoint {
acc: number; acc: number;
...@@ -120,14 +91,6 @@ interface ParaObj { ...@@ -120,14 +91,6 @@ interface ParaObj {
parallelAxis: Array<Dimobj>; parallelAxis: Array<Dimobj>;
} }
interface Intermedia {
name: string; // id
type: string;
data: Array<number | object>; // intermediate data
hyperPara: object; // each trial hyperpara value
trialNum: number;
}
interface MetricDataRecord { interface MetricDataRecord {
timestamp: number; timestamp: number;
trialJobId: string; trialJobId: string;
...@@ -223,20 +186,25 @@ interface SearchItems { ...@@ -223,20 +186,25 @@ interface SearchItems {
isChoice: boolean; // for parameters: type = choice and status also as choice type isChoice: boolean; // for parameters: type = choice and status also as choice type
} }
interface allTrialsIntermediateChart {
name: string;
// id: string;
sequenceId: number;
data: number[];
parameter: object;
type: string;
}
export { export {
TableObj,
TableRecord, TableRecord,
SearchSpace, SearchSpace,
FinalType, FinalType,
ErrorParameter,
Parameters,
AccurPoint, AccurPoint,
DetailAccurPoint, DetailAccurPoint,
TooltipForIntermediate, TooltipForIntermediate,
TooltipForAccuracy, TooltipForAccuracy,
Dimobj, Dimobj,
ParaObj, ParaObj,
Intermedia,
MetricDataRecord, MetricDataRecord,
TrialJobInfo, TrialJobInfo,
ExperimentProfile, ExperimentProfile,
...@@ -248,5 +216,6 @@ export { ...@@ -248,5 +216,6 @@ export {
SortInfo, SortInfo,
AllExperimentList, AllExperimentList,
Tensorboard, Tensorboard,
SearchItems SearchItems,
allTrialsIntermediateChart
}; };
...@@ -51,7 +51,6 @@ class Experiment { ...@@ -51,7 +51,6 @@ class Experiment {
private profileField?: ExperimentProfile; private profileField?: ExperimentProfile;
private metadataField?: ExperimentMetadata = undefined; private metadataField?: ExperimentMetadata = undefined;
private statusField?: NNIManagerStatus = undefined; private statusField?: NNIManagerStatus = undefined;
private isNestedExperiment: boolean = false;
private isexperimentError: boolean = false; private isexperimentError: boolean = false;
private experimentErrorMessage: string = ''; private experimentErrorMessage: string = '';
private isStatusError: boolean = false; private isStatusError: boolean = false;
......
import { SingleAxis, MultipleAxes, TableObj } from '../interface'; import { SingleAxis, MultipleAxes } from '../interface';
import { Trial } from './trial';
import { SUPPORTED_SEARCH_SPACE_TYPE } from '../const'; import { SUPPORTED_SEARCH_SPACE_TYPE } from '../const';
import { formatComplexTypeValue } from '../function'; import { formatComplexTypeValue } from '../function';
...@@ -111,7 +112,7 @@ export class SearchSpace implements MultipleAxes { ...@@ -111,7 +112,7 @@ export class SearchSpace implements MultipleAxes {
}); });
} }
static inferFromTrials(searchSpace: SearchSpace, trials: TableObj[]): SearchSpace { static inferFromTrials(searchSpace: SearchSpace, trials: Trial[]): SearchSpace {
const newSearchSpace = new SearchSpace(searchSpace.baseName, searchSpace.fullName, undefined); const newSearchSpace = new SearchSpace(searchSpace.baseName, searchSpace.fullName, undefined);
for (const [k, v] of searchSpace.axes) { for (const [k, v] of searchSpace.axes) {
newSearchSpace.axes.set(k, v); newSearchSpace.axes.set(k, v);
...@@ -153,7 +154,7 @@ export class MetricSpace implements MultipleAxes { ...@@ -153,7 +154,7 @@ export class MetricSpace implements MultipleAxes {
baseName = ''; baseName = '';
fullName = ''; fullName = '';
constructor(trials: TableObj[]) { constructor(trials: Trial[]) {
const columns = new Map<string, any[]>(); const columns = new Map<string, any[]>();
for (const trial of trials) { for (const trial of trials) {
if (trial.acc === undefined) { if (trial.acc === undefined) {
......
import * as JSON5 from 'json5'; import { MetricDataRecord, TrialJobInfo, TableRecord, FinalType, MultipleAxes, SingleAxis } from '../interface';
import {
MetricDataRecord,
TrialJobInfo,
TableObj,
TableRecord,
Parameters,
FinalType,
MultipleAxes,
SingleAxis
} from '../interface';
import { import {
getFinal, getFinal,
formatAccuracy, formatAccuracy,
...@@ -59,12 +49,11 @@ function inferTrialParameters( ...@@ -59,12 +49,11 @@ function inferTrialParameters(
return [parameters, unexpectedEntries]; return [parameters, unexpectedEntries];
} }
class Trial implements TableObj { class Trial {
private metricsInitialized: boolean = false; private metricsInitialized: boolean = false;
private infoField: TrialJobInfo | undefined; private infoField: TrialJobInfo | undefined;
public accuracy: number | undefined; // trial default metric val: number value or undefined
public intermediates: (MetricDataRecord | undefined)[] = []; public intermediates: (MetricDataRecord | undefined)[] = [];
public final: MetricDataRecord | undefined;
private finalAcc: number | undefined;
constructor(info?: TrialJobInfo, metrics?: MetricDataRecord[]) { constructor(info?: TrialJobInfo, metrics?: MetricDataRecord[]) {
this.infoField = info; this.infoField = info;
...@@ -78,7 +67,7 @@ class Trial implements TableObj { ...@@ -78,7 +67,7 @@ class Trial implements TableObj {
return undefined; return undefined;
} }
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return this.finalAcc! - otherTrial.finalAcc!; return this.accuracy! - otherTrial.accuracy!;
} }
get info(): TrialJobInfo { get info(): TrialJobInfo {
...@@ -86,25 +75,58 @@ class Trial implements TableObj { ...@@ -86,25 +75,58 @@ class Trial implements TableObj {
return this.infoField!; return this.infoField!;
} }
get intermediateMetrics(): MetricDataRecord[] { get sequenceId(): number {
const ret: MetricDataRecord[] = []; return this.info.sequenceId;
for (let i = 0; i < this.intermediates.length; i++) { }
if (this.intermediates[i]) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion get id(): string {
ret.push(this.intermediates[i]!); return this.info.trialJobId;
} else { }
break;
} get duration(): number {
const endTime = this.info.endTime || new Date().getTime();
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return (endTime - this.info.startTime!) / 1000;
}
get status(): string {
return this.info.status;
}
get parameter(): object {
return JSON.parse(this.info.hyperParameters![0]).parameters;
}
// return dict final result: {default: xxx...}
get acc(): FinalType | undefined {
if (this.info === undefined) {
return undefined;
} }
return ret; return getFinal(this.info.finalMetricData);
} }
get accuracy(): number | undefined { public parameters(axes: MultipleAxes): Map<SingleAxis, any> {
return this.finalAcc; const ret = new Map<SingleAxis, any>(Array.from(axes.axes.values()).map(k => [k, null]));
if (this.info === undefined || this.info.hyperParameters === undefined) {
throw ret;
} else {
let params = JSON.parse(this.info.hyperParameters[0]).parameters;
if (typeof params === 'string') {
params = JSON.parse(params);
}
const [updated, unexpectedEntries] = inferTrialParameters(params, axes);
if (unexpectedEntries.size) {
throw unexpectedEntries;
}
for (const [k, v] of updated) {
ret.set(k, v);
}
return ret;
}
} }
get sortable(): boolean { get sortable(): boolean {
return this.metricsInitialized && this.finalAcc !== undefined && isFinite(this.finalAcc); return this.metricsInitialized && this.accuracy !== undefined && isFinite(this.accuracy);
} }
get latestAccuracy(): number | undefined { get latestAccuracy(): number | undefined {
...@@ -131,20 +153,29 @@ class Trial implements TableObj { ...@@ -131,20 +153,29 @@ class Trial implements TableObj {
return undefined; return undefined;
} }
} }
get accuracyNumberTypeDictKeys(): string[] {
let accuracyTypeList: string[] = [];
if (this.acc !== undefined) {
for (const [item, value] of Object.entries(this.acc)) {
if (typeof value === 'number') {
accuracyTypeList.push(item);
}
}
} else {
accuracyTypeList = ['default'];
}
return accuracyTypeList;
}
/* table obj start */ /* table obj start */
get tableRecord(): TableRecord { get tableRecord(): TableRecord {
const endTime = this.info.endTime || new Date().getTime(); const endTime = this.info.endTime || new Date().getTime();
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const duration = (endTime - this.info.startTime!) / 1000; const duration = (endTime - this.info.startTime!) / 1000;
let accuracy;
if (this.acc !== undefined && this.acc.default !== undefined) {
if (typeof this.acc.default === 'number') {
accuracy = JSON5.parse(this.acc.default);
} else {
accuracy = this.acc.default;
}
}
return { return {
key: this.info.trialJobId, key: this.info.trialJobId,
...@@ -155,114 +186,13 @@ class Trial implements TableObj { ...@@ -155,114 +186,13 @@ class Trial implements TableObj {
endTime: this.info.endTime, endTime: this.info.endTime,
duration, duration,
status: this.info.status, status: this.info.status,
message: this.info.message || '--', message: this.info.message ?? '--',
intermediateCount: this.intermediates.length, intermediateCount: this.intermediates.length,
accuracy: accuracy,
latestAccuracy: this.latestAccuracy, latestAccuracy: this.latestAccuracy,
formattedLatestAccuracy: this.formatLatestAccuracy() formattedLatestAccuracy: this.formatLatestAccuracy()
}; };
} }
get key(): number {
return this.info.sequenceId;
}
get sequenceId(): number {
return this.info.sequenceId;
}
get id(): string {
return this.info.trialJobId;
}
get duration(): number {
const endTime = this.info.endTime || new Date().getTime();
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return (endTime - this.info.startTime!) / 1000;
}
get status(): string {
return this.info.status;
}
get acc(): FinalType | undefined {
if (this.info === undefined) {
return undefined;
}
return getFinal(this.info.finalMetricData);
}
get accuracyNumberTypeDictKeys(): string[] {
let accuracyTypeList: string[] = [];
if (this.acc !== undefined) {
for (const [item, value] of Object.entries(this.acc)) {
if (typeof value === 'number') {
accuracyTypeList.push(item);
}
}
} else {
accuracyTypeList = ['default'];
}
return accuracyTypeList;
}
get description(): Parameters {
const ret: Parameters = {
parameters: {},
intermediate: [],
multiProgress: 1
};
const tempHyper = this.info.hyperParameters;
if (tempHyper !== undefined) {
const getPara = JSON.parse(tempHyper[tempHyper.length - 1]).parameters;
ret.multiProgress = tempHyper.length;
if (typeof getPara === 'string') {
ret.parameters = JSON.parse(getPara);
} else {
ret.parameters = getPara;
}
} else {
ret.parameters = { error: "This trial's parameters are not available." };
}
if (this.info.logPath !== undefined) {
ret.logPath = this.info.logPath;
}
const mediate: number[] = [];
for (const items of this.intermediateMetrics) {
if (typeof parseMetrics(items.data) === 'object') {
mediate.push(parseMetrics(items.data).default);
} else {
mediate.push(parseMetrics(items.data));
}
}
ret.intermediate = mediate;
return ret;
}
public parameters(axes: MultipleAxes): Map<SingleAxis, any> {
const ret = new Map<SingleAxis, any>(Array.from(axes.axes.values()).map(k => [k, null]));
if (this.info === undefined || this.info.hyperParameters === undefined) {
throw ret;
} else {
const tempHyper = this.info.hyperParameters;
let params = JSON.parse(tempHyper[tempHyper.length - 1]).parameters;
if (typeof params === 'string') {
params = JSON.parse(params);
}
const [updated, unexpectedEntries] = inferTrialParameters(params, axes);
if (unexpectedEntries.size) {
throw unexpectedEntries;
}
for (const [k, v] of updated) {
ret.set(k, v);
}
return ret;
}
}
public metrics(space: MultipleAxes): Map<SingleAxis, any> { public metrics(space: MultipleAxes): Map<SingleAxis, any> {
// set default value: null // set default value: null
const ret = new Map<SingleAxis, any>(Array.from(space.axes.values()).map(k => [k, null])); const ret = new Map<SingleAxis, any>(Array.from(space.axes.values()).map(k => [k, null]));
...@@ -270,8 +200,7 @@ class Trial implements TableObj { ...@@ -270,8 +200,7 @@ class Trial implements TableObj {
if (this.acc === undefined) { if (this.acc === undefined) {
return ret; return ret;
} }
const acc = typeof this.acc === 'number' ? { default: this.acc } : this.acc; Object.entries(this.acc).forEach(item => {
Object.entries(acc).forEach(item => {
const [k, v] = item; const [k, v] = item;
const column = space.axes.get(k); const column = space.axes.get(k);
...@@ -287,14 +216,6 @@ class Trial implements TableObj { ...@@ -287,14 +216,6 @@ class Trial implements TableObj {
return ret; return ret;
} }
public finalKeys(): string[] {
if (this.acc !== undefined) {
return Object.keys(this.acc);
} else {
return [];
}
}
/* table obj end */ /* table obj end */
public initialized(): boolean { public initialized(): boolean {
...@@ -304,7 +225,7 @@ class Trial implements TableObj { ...@@ -304,7 +225,7 @@ class Trial implements TableObj {
public updateMetrics(metrics: MetricDataRecord[]): boolean { public updateMetrics(metrics: MetricDataRecord[]): boolean {
// parameter `metrics` must contain all known metrics of this trial // parameter `metrics` must contain all known metrics of this trial
this.metricsInitialized = true; this.metricsInitialized = true;
const prevMetricCnt = this.intermediates.length + (this.final ? 1 : 0); const prevMetricCnt = this.intermediates.length + (this.accuracy ? 1 : 0);
if (metrics.length <= prevMetricCnt) { if (metrics.length <= prevMetricCnt) {
return false; return false;
} }
...@@ -312,8 +233,7 @@ class Trial implements TableObj { ...@@ -312,8 +233,7 @@ class Trial implements TableObj {
if (metric.type === 'PERIODICAL') { if (metric.type === 'PERIODICAL') {
this.intermediates[metric.sequence] = metric; this.intermediates[metric.sequence] = metric;
} else { } else {
this.final = metric; this.accuracy = metricAccuracy(metric);
this.finalAcc = metricAccuracy(metric);
} }
} }
return true; return true;
...@@ -328,9 +248,8 @@ class Trial implements TableObj { ...@@ -328,9 +248,8 @@ class Trial implements TableObj {
updated = updated || !this.intermediates[metric.sequence]; updated = updated || !this.intermediates[metric.sequence];
this.intermediates[metric.sequence] = metric; this.intermediates[metric.sequence] = metric;
} else { } else {
updated = updated || !this.final; updated = updated || !this.accuracy;
this.final = metric; this.accuracy = metricAccuracy(metric);
this.finalAcc = metricAccuracy(metric);
} }
} }
return updated; return updated;
...@@ -340,13 +259,20 @@ class Trial implements TableObj { ...@@ -340,13 +259,20 @@ class Trial implements TableObj {
const same = this.infoField && this.infoField.status === trialJobInfo.status; const same = this.infoField && this.infoField.status === trialJobInfo.status;
this.infoField = trialJobInfo; this.infoField = trialJobInfo;
if (trialJobInfo.finalMetricData) { if (trialJobInfo.finalMetricData) {
this.final = trialJobInfo.finalMetricData[trialJobInfo.finalMetricData.length - 1]; this.accuracy = metricAccuracy(trialJobInfo.finalMetricData[0]);
this.finalAcc = metricAccuracy(this.final);
} }
return !same; return !same;
} }
private renderNumber(val: any): string { /**
*
* @param val trial latest accuracy
* @returns 0.9(FINAL) or 0.9(LATEST)
* NaN or Infinity
* string object such as: '{tensor: {data}}'
*
*/
private formatLatestAccuracyToString(val: any): string {
if (typeof val === 'number') { if (typeof val === 'number') {
if (isNaNorInfinity(val)) { if (isNaNorInfinity(val)) {
return `${val}`; // show 'NaN' or 'Infinity' return `${val}`; // show 'NaN' or 'Infinity'
...@@ -363,19 +289,27 @@ class Trial implements TableObj { ...@@ -363,19 +289,27 @@ class Trial implements TableObj {
} }
} }
public formatLatestAccuracy(): string { /**
// TODO: this should be private *
* @param val trial latest accuracy
* @returns 0.9(FINAL) or 0.9(LATEST)
* NaN or Infinity
* string object such as: '{tensor: {data}}'
* +1 describe type undefined: --
*
*/
private formatLatestAccuracy(): string {
if (this.status === 'SUCCEEDED') { if (this.status === 'SUCCEEDED') {
return this.accuracy === undefined ? '--' : this.renderNumber(this.accuracy); return this.accuracy === undefined ? '--' : this.formatLatestAccuracyToString(this.accuracy);
} else { } else {
if (this.accuracy !== undefined) { if (this.accuracy !== undefined) {
return this.renderNumber(this.accuracy); return this.formatLatestAccuracyToString(this.accuracy);
} else if (this.intermediates.length === 0) { } else if (this.intermediates.length === 0) {
return '--'; return '--';
} else { } else {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const latest = this.intermediates[this.intermediates.length - 1]!; const latest = this.intermediates[this.intermediates.length - 1]!;
return this.renderNumber(metricAccuracy(latest)); return this.formatLatestAccuracyToString(metricAccuracy(latest));
} }
} }
} }
......
...@@ -2,7 +2,8 @@ import { MANAGER_IP, METRIC_GROUP_UPDATE_THRESHOLD, METRIC_GROUP_UPDATE_SIZE } f ...@@ -2,7 +2,8 @@ import { MANAGER_IP, METRIC_GROUP_UPDATE_THRESHOLD, METRIC_GROUP_UPDATE_SIZE } f
import { MetricDataRecord, TableRecord, TrialJobInfo, MultipleAxes } from '../interface'; import { MetricDataRecord, TableRecord, TrialJobInfo, MultipleAxes } from '../interface';
import { Trial } from './trial'; import { Trial } from './trial';
import { SearchSpace, MetricSpace } from './searchspace'; import { SearchSpace, MetricSpace } from './searchspace';
import { requestAxios } from '../function'; import { requestAxios, parseMetrics } from '../function';
import { allTrialsIntermediateChart } from '../interface';
function groupMetricsByTrial(metrics: MetricDataRecord[]): Map<string, MetricDataRecord[]> { function groupMetricsByTrial(metrics: MetricDataRecord[]): Map<string, MetricDataRecord[]> {
const ret = new Map<string, MetricDataRecord[]>(); const ret = new Map<string, MetricDataRecord[]>();
...@@ -86,10 +87,37 @@ class TrialManager { ...@@ -86,10 +87,37 @@ class TrialManager {
return this.filter(trial => trial.status === 'SUCCEEDED'); return this.filter(trial => trial.status === 'SUCCEEDED');
} }
public notWaittingTrials(): Trial[] {
return this.filter(trial => trial.status !== 'WAITING');
}
public allTrialsIntermediateChart(): allTrialsIntermediateChart[] {
const ret: allTrialsIntermediateChart[] = [];
for (const trial of this.trials.values()) {
const mediate: number[] = [];
for (const items of trial.intermediates) {
if (typeof parseMetrics(items!.data) === 'object') {
mediate.push(parseMetrics(items!.data).default);
} else {
mediate.push(parseMetrics(items!.data));
}
}
ret.push({
name: trial.id,
sequenceId: trial.sequenceId,
data: mediate,
parameter: trial.parameter,
type: 'line'
});
}
return ret;
}
public finalKeys(): string[] { public finalKeys(): string[] {
const succeedTrialsList = this.filter(trial => trial.status === 'SUCCEEDED'); const succeedTrialsList = this.filter(trial => trial.status === 'SUCCEEDED');
if (succeedTrialsList !== undefined && succeedTrialsList[0] !== undefined) { if (succeedTrialsList !== undefined && succeedTrialsList[0] !== undefined) {
return succeedTrialsList[0].finalKeys(); return succeedTrialsList[0].acc !== undefined ? Object.keys(succeedTrialsList[0].acc) : [];
} else { } else {
return ['default']; return ['default'];
} }
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment