Unverified Commit 12410686 authored by chicm-ms's avatar chicm-ms Committed by GitHub
Browse files

Merge pull request #20 from microsoft/master

pull code
parents 611a45fc 61fec446
......@@ -67,7 +67,7 @@ def get_next_parameter():
params_file_name = 'parameter.cfg'
else:
raise AssertionError('_param_index value ({}) should >=0'.format(_param_index))
params_filepath = os.path.join(_sysdir, params_file_name)
if not os.path.isfile(params_filepath):
request_next_parameter()
......@@ -81,11 +81,11 @@ def get_next_parameter():
def send_metric(string):
if _nni_platform != 'local':
data = (string).encode('utf8')
assert len(data) < 1000000, 'Metric too long'
assert len(data) < 1000000, 'Metric too long'
print('NNISDK_ME%s' % (data), flush=True)
else:
data = (string + '\n').encode('utf8')
assert len(data) < 1000000, 'Metric too long'
assert len(data) < 1000000, 'Metric too long'
_metric_file.write(b'ME%06d%b' % (len(data), data))
_metric_file.flush()
if sys.platform == "win32":
......
......@@ -24,12 +24,12 @@ import numpy as np
def get_json_content(file_path):
"""Load json file content
Parameters
----------
file_path:
path to the file
Raises
------
TypeError
......@@ -43,9 +43,9 @@ def get_json_content(file_path):
return None
def generate_pcs(nni_search_space_content):
"""Generate the Parameter Configuration Space (PCS) which defines the
"""Generate the Parameter Configuration Space (PCS) which defines the
legal ranges of the parameters to be optimized and their default values.
Generally, the format is:
# parameter_name categorical {value_1, ..., value_N} [default value]
# parameter_name ordinal {value_1, ..., value_N} [default value]
......@@ -53,14 +53,14 @@ def generate_pcs(nni_search_space_content):
# parameter_name integer [min_value, max_value] [default value] log
# parameter_name real [min_value, max_value] [default value]
# parameter_name real [min_value, max_value] [default value] log
Reference: https://automl.github.io/SMAC3/stable/options.html
Parameters
----------
nni_search_space_content: search_space
The search space in this experiment in nni
Returns
-------
Parameter Configuration Space (PCS)
......@@ -81,8 +81,8 @@ def generate_pcs(nni_search_space_content):
if search_space[key]['_type'] == 'choice':
choice_len = len(search_space[key]['_value'])
pcs_fd.write('%s categorical {%s} [%s]\n' % (
key,
json.dumps(list(range(choice_len)))[1:-1],
key,
json.dumps(list(range(choice_len)))[1:-1],
json.dumps(0)))
if key in categorical_dict:
raise RuntimeError('%s has already existed, please make sure search space has no duplicate key.' % key)
......@@ -90,19 +90,19 @@ def generate_pcs(nni_search_space_content):
elif search_space[key]['_type'] == 'randint':
# TODO: support lower bound in randint
pcs_fd.write('%s integer [0, %d] [%d]\n' % (
key,
search_space[key]['_value'][0],
key,
search_space[key]['_value'][0],
search_space[key]['_value'][0]))
elif search_space[key]['_type'] == 'uniform':
pcs_fd.write('%s real %s [%s]\n' % (
key,
key,
json.dumps(search_space[key]['_value']),
json.dumps(search_space[key]['_value'][0])))
elif search_space[key]['_type'] == 'loguniform':
# use np.round here to ensure that the rounded defaut value is in the range, which will be rounded in configure_space package
search_space[key]['_value'] = list(np.round(np.log(search_space[key]['_value']), 10))
pcs_fd.write('%s real %s [%s]\n' % (
key,
key,
json.dumps(search_space[key]['_value']),
json.dumps(search_space[key]['_value'][0])))
elif search_space[key]['_type'] == 'quniform' \
......@@ -122,9 +122,9 @@ def generate_pcs(nni_search_space_content):
return None
def generate_scenario(ss_content):
"""Generate the scenario. The scenario-object (smac.scenario.scenario.Scenario) is used to configure SMAC and
"""Generate the scenario. The scenario-object (smac.scenario.scenario.Scenario) is used to configure SMAC and
can be constructed either by providing an actual scenario-object, or by specifing the options in a scenario file.
Reference: https://automl.github.io/SMAC3/stable/options.html
The format of the scenario file is one option per line:
......@@ -135,7 +135,7 @@ def generate_scenario(ss_content):
Parameters
----------
abort_on_first_run_crash: bool
If true, SMAC will abort if the first run of the target algorithm crashes. Default: True,
If true, SMAC will abort if the first run of the target algorithm crashes. Default: True,
because trials reported to nni tuner would always in success state
algo: function
Specifies the target algorithm call that SMAC will optimize. Interpreted as a bash-command.
......
......@@ -64,7 +64,7 @@ class SMACTuner(Tuner):
def _main_cli(self):
"""Main function of SMAC for CLI interface
Returns
-------
instance
......@@ -153,7 +153,7 @@ class SMACTuner(Tuner):
def receive_trial_result(self, parameter_id, parameters, value):
"""receive_trial_result
Parameters
----------
parameter_id: int
......@@ -162,7 +162,7 @@ class SMACTuner(Tuner):
parameters
value:
value
Raises
------
RuntimeError
......@@ -185,7 +185,7 @@ class SMACTuner(Tuner):
Also, we convert categorical:
categorical values in search space are changed to list of numbers before,
those original values will be changed back in this function
Parameters
----------
challenger_dict: dict
......@@ -211,12 +211,12 @@ class SMACTuner(Tuner):
def generate_parameters(self, parameter_id):
"""generate one instance of hyperparameters
Parameters
----------
parameter_id: int
parameter id
Returns
-------
list
......@@ -234,12 +234,12 @@ class SMACTuner(Tuner):
def generate_multiple_parameters(self, parameter_id_list):
"""generate mutiple instances of hyperparameters
Parameters
----------
parameter_id_list: list
list of parameter id
Returns
-------
list
......
......@@ -124,7 +124,7 @@ else:
funcs_args,
fixed_inputs,
optional_inputs,
optional_input_size=0):
optional_input_size):
'''execute the chosen function and inputs.
Below is an example of chosen function and inputs:
{
......@@ -149,7 +149,7 @@ else:
chosen_layer = mutable_block[mutable_layer_id]["chosen_layer"]
chosen_inputs = mutable_block[mutable_layer_id]["chosen_inputs"]
real_chosen_inputs = [optional_inputs[input_name] for input_name in chosen_inputs]
layer_out = funcs[chosen_layer]([fixed_inputs, real_chosen_inputs], *funcs_args[chosen_layer])
layer_out = funcs[chosen_layer]([fixed_inputs, real_chosen_inputs], **funcs_args[chosen_layer])
return layer_out
......
......@@ -32,7 +32,7 @@ from nni.multi_phase.multi_phase_dispatcher import MultiPhaseMsgDispatcher
from unittest import TestCase, main
class NaiveMultiPhaseTuner(MultiPhaseTuner):
'''
'''
supports only choices
'''
def __init__(self):
......
......@@ -40,7 +40,7 @@ class TrialTestCase(TestCase):
def test_get_sequence_id(self):
self.assertEqual(nni.get_sequence_id(), 0)
def test_report_intermediate_result(self):
nni.report_intermediate_result(123)
self.assertEqual(test_platform.get_last_metric(), {
......
......@@ -9,11 +9,12 @@
"copy-to-clipboard": "^3.0.8",
"echarts": "^4.1.0",
"echarts-for-react": "^2.0.14",
"react": "^16.4.2",
"react-dom": "^16.4.2",
"react": "^16.7.0-alpha.2",
"react-dom": "^16.7.0-alpha.2",
"react-json-tree": "^0.11.0",
"react-monaco-editor": "^0.22.0",
"react-router": "3.2.1",
"react-responsive": "^7.0.0",
"react-scripts-ts-antd": "2.17.0"
},
"scripts": {
......@@ -28,7 +29,8 @@
"@types/react": "^16.4.17",
"@types/react-dom": "^16.0.7",
"@types/react-json-tree": "^0.6.8",
"@types/react-responsive": "^3.0.3",
"@types/react-router": "3.0.15",
"typescript": "^3.0.1"
}
}
}
\ No newline at end of file
......@@ -10,14 +10,12 @@
left: 0;
top: 0;
width: 100%;
height: 56px;
height: 56px;
background: #0071BC;
border-right: 1px solid #ccc;
z-index: 1000;
}
.headerCon{
min-width: 1024px;
}
.contentBox{
width: 100%;
}
......@@ -29,5 +27,3 @@
margin-bottom: 30px;
background: #fff;
}
......@@ -3,20 +3,59 @@ import { Row, Col } from 'antd';
import './App.css';
import SlideBar from './components/SlideBar';
class App extends React.Component<{}, {}> {
interface AppState {
interval: number;
whichPageToFresh: string;
}
class App extends React.Component<{}, AppState> {
public _isMounted: boolean;
constructor(props: {}) {
super(props);
this.state = {
interval: 10, // sendons
whichPageToFresh: ''
};
}
changeInterval = (interval: number) => {
if (this._isMounted === true) {
this.setState(() => ({ interval: interval }));
}
}
changeFresh = (fresh: string) => {
// interval * 1000
if (this._isMounted === true) {
this.setState(() => ({ whichPageToFresh: fresh }));
}
}
componentDidMount() {
this._isMounted = true;
}
componentWillUnmount() {
this._isMounted = false;
}
render() {
const { interval, whichPageToFresh } = this.state;
const reactPropsChildren = React.Children.map(this.props.children, child =>
// tslint:disable-next-line:no-any
React.cloneElement(child as React.ReactElement<any>, { interval, whichPageToFresh })
);
return (
<Row className="nni" style={{minHeight: window.innerHeight}}>
<Row className="nni" style={{ minHeight: window.innerHeight }}>
<Row className="header">
<Col span={1} />
<Col className="headerCon" span={22}>
<SlideBar />
<SlideBar changeInterval={this.changeInterval} changeFresh={this.changeFresh}/>
</Col>
<Col span={1} />
</Row>
<Row className="contentBox">
<Row className="content">
{this.props.children}
{reactPropsChildren}
</Row>
</Row>
</Row>
......
......@@ -39,13 +39,18 @@ interface OverviewState {
isMultiPhase: boolean;
}
class Overview extends React.Component<{}, OverviewState> {
interface OverviewProps {
interval: number; // user select
whichPageToFresh: string;
}
class Overview extends React.Component<OverviewProps, OverviewState> {
public _isMounted = false;
public intervalID = 0;
public intervalProfile = 1;
constructor(props: {}) {
constructor(props: OverviewProps) {
super(props);
this.state = {
searchSpace: {},
......@@ -377,17 +382,19 @@ class Overview extends React.Component<{}, OverviewState> {
data: accarr
}]
};
this.setState({ accuracyData: accOption }, () => {
if (accarr.length === 0) {
this.setState({
accNodata: 'No data'
});
} else {
this.setState({
accNodata: ''
});
}
});
if (this._isMounted) {
this.setState({ accuracyData: accOption }, () => {
if (accarr.length === 0) {
this.setState({
accNodata: 'No data'
});
} else {
this.setState({
accNodata: ''
});
}
});
}
}
clickMaxTop = (event: React.SyntheticEvent<EventTarget>) => {
......@@ -405,23 +412,39 @@ class Overview extends React.Component<{}, OverviewState> {
isOffInterval = () => {
const { status } = this.state;
switch (status) {
case 'DONE':
case 'ERROR':
case 'STOPPED':
window.clearInterval(this.intervalID);
window.clearInterval(this.intervalProfile);
break;
default:
const { interval } = this.props;
if (status === 'DONE' || status === 'ERROR' || status === 'STOPPED' ||
interval === 0
) {
window.clearInterval(this.intervalID);
window.clearInterval(this.intervalProfile);
return;
}
}
componentWillReceiveProps(nextProps: OverviewProps) {
const { interval, whichPageToFresh } = nextProps;
window.clearInterval(this.intervalID);
window.clearInterval(this.intervalProfile);
if (whichPageToFresh.includes('/oview')) {
this.showTrials();
this.showSessionPro();
}
if (interval !== 0) {
this.intervalID = window.setInterval(this.showTrials, interval * 1000);
this.intervalProfile = window.setInterval(this.showSessionPro, interval * 1000);
}
}
componentDidMount() {
this._isMounted = true;
this.showSessionPro();
const { interval } = this.props;
this.showTrials();
this.intervalID = window.setInterval(this.showTrials, 10000);
this.intervalProfile = window.setInterval(this.showSessionPro, 60000);
this.showSessionPro();
if (interval !== 0) {
this.intervalID = window.setInterval(this.showTrials, interval * 1000);
this.intervalProfile = window.setInterval(this.showSessionPro, interval * 1000);
}
}
componentWillUnmount() {
......@@ -447,7 +470,7 @@ class Overview extends React.Component<{}, OverviewState> {
</Row>
<Row className="overMessage">
{/* status graph */}
<Col span={9} className="prograph overviewBoder">
<Col span={9} className="prograph overviewBoder cc">
<Title1 text="Status" icon="5.png" />
<Progressed
trialNumber={trialNumber}
......@@ -459,13 +482,13 @@ class Overview extends React.Component<{}, OverviewState> {
/>
</Col>
{/* experiment parameters search space tuner assessor... */}
<Col span={7} className="overviewBoder">
<Col span={7} className="overviewBoder cc">
<Title1 text="Search space" icon="10.png" />
<Row className="experiment">
<SearchSpace searchSpace={searchSpace} />
</Row>
</Col>
<Col span={8} className="overviewBoder">
<Col span={8} className="overviewBoder cc">
<Title1 text="Profile" icon="4.png" />
<Row className="experiment">
{/* the scroll bar all the trial profile in the searchSpace div*/}
......
import * as React from 'react';
import { Link } from 'react-router';
import axios from 'axios';
import { DOWNLOAD_IP } from '../static/const';
import { Row, Col, Menu, Dropdown, Icon } from 'antd';
import { MANAGER_IP } from '../static/const';
import MediaQuery from 'react-responsive';
import { DOWNLOAD_IP } from '../static/const';
import { Row, Col, Menu, Dropdown, Icon, Select } from 'antd';
const { SubMenu } = Menu;
const { Option } = Select;
import '../static/style/slideBar.scss';
import '../static/style/button.scss';
interface SliderState {
version: string;
menuVisible: boolean;
version: string;
menuVisible: boolean;
navBarVisible: boolean;
}
interface SliderProps {
changeInterval: (value: number) => void;
changeFresh: (value: string) => void;
}
interface EventPer {
key: string;
key: string;
}
class SlideBar extends React.Component<{}, SliderState> {
public _isMounted = false;
constructor(props: {}) {
super(props);
this.state = {
version: '',
menuVisible: false
};
}
downExperimentContent = () => {
axios
.all([
axios.get(`${MANAGER_IP}/experiment`),
axios.get(`${MANAGER_IP}/trial-jobs`),
axios.get(`${MANAGER_IP}/metric-data`)
])
.then(axios.spread((res, res1, res2) => {
if (res.status === 200 && res1.status === 200 && res2.status === 200) {
if (res.data.params.searchSpace) {
res.data.params.searchSpace = JSON.parse(res.data.params.searchSpace);
}
const isEdge = navigator.userAgent.indexOf('Edge') !== -1 ? true : false;
let trialMessagesArr = res1.data;
const interResultList = res2.data;
Object.keys(trialMessagesArr).map(item => {
// transform hyperparameters as object to show elegantly
trialMessagesArr[item].hyperParameters = JSON.parse(trialMessagesArr[item].hyperParameters);
const trialId = trialMessagesArr[item].id;
// add intermediate result message
trialMessagesArr[item].intermediate = [];
Object.keys(interResultList).map(key => {
const interId = interResultList[key].trialJobId;
if (trialId === interId) {
trialMessagesArr[item].intermediate.push(interResultList[key]);
}
class SlideBar extends React.Component<SliderProps, SliderState> {
public _isMounted = false;
public divMenu: HTMLDivElement | null;
public countOfMenu: number = 0;
public selectHTML: Select | null;
constructor(props: SliderProps) {
super(props);
this.state = {
version: '',
menuVisible: false,
navBarVisible: false,
};
}
downExperimentContent = () => {
axios
.all([
axios.get(`${MANAGER_IP}/experiment`),
axios.get(`${MANAGER_IP}/trial-jobs`),
axios.get(`${MANAGER_IP}/metric-data`)
])
.then(axios.spread((res, res1, res2) => {
if (res.status === 200 && res1.status === 200 && res2.status === 200) {
if (res.data.params.searchSpace) {
res.data.params.searchSpace = JSON.parse(res.data.params.searchSpace);
}
const isEdge = navigator.userAgent.indexOf('Edge') !== -1 ? true : false;
let trialMessagesArr = res1.data;
const interResultList = res2.data;
Object.keys(trialMessagesArr).map(item => {
// transform hyperparameters as object to show elegantly
trialMessagesArr[item].hyperParameters = JSON.parse(trialMessagesArr[item].hyperParameters);
const trialId = trialMessagesArr[item].id;
// add intermediate result message
trialMessagesArr[item].intermediate = [];
Object.keys(interResultList).map(key => {
const interId = interResultList[key].trialJobId;
if (trialId === interId) {
trialMessagesArr[item].intermediate.push(interResultList[key]);
}
});
});
const result = {
experimentParameters: res.data,
trialMessage: trialMessagesArr
};
const aTag = document.createElement('a');
const file = new Blob([JSON.stringify(result, null, 4)], { type: 'application/json' });
aTag.download = 'experiment.json';
aTag.href = URL.createObjectURL(file);
aTag.click();
if (!isEdge) {
URL.revokeObjectURL(aTag.href);
}
if (navigator.userAgent.indexOf('Firefox') > -1) {
const downTag = document.createElement('a');
downTag.addEventListener('click', function () {
downTag.download = 'experiment.json';
downTag.href = URL.createObjectURL(file);
});
let eventMouse = document.createEvent('MouseEvents');
eventMouse.initEvent('click', false, false);
downTag.dispatchEvent(eventMouse);
}
}
}));
}
downnnimanagerLog = () => {
axios(`${DOWNLOAD_IP}/nnimanager.log`, {
method: 'GET'
})
.then(res => {
if (res.status === 200) {
const nniLogfile = res.data;
const aTag = document.createElement('a');
const isEdge = navigator.userAgent.indexOf('Edge') !== -1 ? true : false;
const file = new Blob([nniLogfile], { type: 'application/json' });
aTag.download = 'nnimanagerLog.log';
aTag.href = URL.createObjectURL(file);
aTag.click();
if (!isEdge) {
URL.revokeObjectURL(aTag.href);
}
if (navigator.userAgent.indexOf('Firefox') > -1) {
const downTag = document.createElement('a');
downTag.addEventListener('click', function () {
downTag.download = 'nnimanagerLog.log';
downTag.href = URL.createObjectURL(file);
});
let eventMouse = document.createEvent('MouseEvents');
eventMouse.initEvent('click', false, false);
downTag.dispatchEvent(eventMouse);
}
}
});
});
const result = {
experimentParameters: res.data,
trialMessage: trialMessagesArr
};
const aTag = document.createElement('a');
const file = new Blob([JSON.stringify(result, null, 4)], { type: 'application/json' });
aTag.download = 'experiment.json';
aTag.href = URL.createObjectURL(file);
aTag.click();
if (!isEdge) {
URL.revokeObjectURL(aTag.href);
}
if (navigator.userAgent.indexOf('Firefox') > -1) {
const downTag = document.createElement('a');
downTag.addEventListener('click', function () {
downTag.download = 'experiment.json';
downTag.href = URL.createObjectURL(file);
}
downDispatcherlog = () => {
axios(`${DOWNLOAD_IP}/dispatcher.log`, {
method: 'GET'
})
.then(res => {
if (res.status === 200) {
const dispatchLogfile = res.data;
const aTag = document.createElement('a');
const isEdge = navigator.userAgent.indexOf('Edge') !== -1 ? true : false;
const file = new Blob([dispatchLogfile], { type: 'application/json' });
aTag.download = 'dispatcherLog.log';
aTag.href = URL.createObjectURL(file);
aTag.click();
if (!isEdge) {
URL.revokeObjectURL(aTag.href);
}
if (navigator.userAgent.indexOf('Firefox') > -1) {
const downTag = document.createElement('a');
downTag.addEventListener('click', function () {
downTag.download = 'dispatcherLog.log';
downTag.href = URL.createObjectURL(file);
});
let eventMouse = document.createEvent('MouseEvents');
eventMouse.initEvent('click', false, false);
downTag.dispatchEvent(eventMouse);
}
}
});
let eventMouse = document.createEvent('MouseEvents');
eventMouse.initEvent('click', false, false);
downTag.dispatchEvent(eventMouse);
}
}
}));
}
downnnimanagerLog = () => {
axios(`${DOWNLOAD_IP}/nnimanager.log`, {
method: 'GET'
})
.then(res => {
if (res.status === 200) {
const nniLogfile = res.data;
const aTag = document.createElement('a');
const isEdge = navigator.userAgent.indexOf('Edge') !== -1 ? true : false;
const file = new Blob([nniLogfile], { type: 'application/json' });
aTag.download = 'nnimanagerLog.json';
aTag.href = URL.createObjectURL(file);
aTag.click();
if (!isEdge) {
URL.revokeObjectURL(aTag.href);
}
if (navigator.userAgent.indexOf('Firefox') > -1) {
const downTag = document.createElement('a');
downTag.addEventListener('click', function () {
downTag.download = 'nnimanagerLog.json';
downTag.href = URL.createObjectURL(file);
}
getNNIversion = () => {
axios(`${MANAGER_IP}/version`, {
method: 'GET'
})
.then(res => {
if (res.status === 200 && this._isMounted) {
this.setState({ version: res.data });
}
});
let eventMouse = document.createEvent('MouseEvents');
eventMouse.initEvent('click', false, false);
downTag.dispatchEvent(eventMouse);
}
}
handleMenuClick = (e: EventPer) => {
if (this._isMounted) { this.setState({ menuVisible: false }); }
switch (e.key) {
// download experiment related content
case '1':
this.downExperimentContent();
break;
// download nnimanager log file
case '2':
this.downnnimanagerLog();
break;
// download dispatcher log file
case '3':
this.downDispatcherlog();
break;
case 'close':
case '10':
case '20':
case '30':
case '60':
this.getInterval(e.key);
break;
default:
}
});
}
downDispatcherlog = () => {
axios(`${DOWNLOAD_IP}/dispatcher.log`, {
method: 'GET'
})
.then(res => {
if (res.status === 200) {
const dispatchLogfile = res.data;
const aTag = document.createElement('a');
const isEdge = navigator.userAgent.indexOf('Edge') !== -1 ? true : false;
const file = new Blob([dispatchLogfile], { type: 'application/json' });
aTag.download = 'dispatcherLog.json';
aTag.href = URL.createObjectURL(file);
aTag.click();
if (!isEdge) {
URL.revokeObjectURL(aTag.href);
}
if (navigator.userAgent.indexOf('Firefox') > -1) {
const downTag = document.createElement('a');
downTag.addEventListener('click', function () {
downTag.download = 'dispatcherLog.json';
downTag.href = URL.createObjectURL(file);
});
let eventMouse = document.createEvent('MouseEvents');
eventMouse.initEvent('click', false, false);
downTag.dispatchEvent(eventMouse);
}
}
handleVisibleChange = (flag: boolean) => {
if (this._isMounted === true) {
this.setState({ menuVisible: flag });
}
});
}
getNNIversion = () => {
axios(`${MANAGER_IP}/version`, {
method: 'GET'
})
.then(res => {
if (res.status === 200 && this._isMounted) {
this.setState({ version: res.data });
}
getInterval = (value: string) => {
if (value === 'close') {
this.props.changeInterval(0);
} else {
this.props.changeInterval(parseInt(value, 10));
}
}
menu = () => {
this.countOfMenu = 0;
return (
<Menu onClick={this.handleMenuClick}>
<Menu.Item key="1">Experiment Parameters</Menu.Item>
<Menu.Item key="2">NNImanager Logfile</Menu.Item>
<Menu.Item key="3">Dispatcher Logfile</Menu.Item>
</Menu>
);
}
// nav bar
navigationBar = () => {
const { version } = this.state;
const feedBackLink = `https://github.com/Microsoft/nni/issues/new?labels=${version}`;
return (
<Menu onClick={this.handleMenuClick} mode="inline">
<Menu.Item key="overview"><Link to={'/oview'}>Overview</Link></Menu.Item>
<Menu.Item key="detail"><Link to={'/detail'}>Trials detail</Link></Menu.Item>
<Menu.Item key="fresh">
<span className="fresh" onClick={this.fresh}><span>Fresh</span></span>
</Menu.Item>
<Menu.Item key="feedback">
<a href={feedBackLink} target="_blank">Feedback</a>
</Menu.Item>
<Menu.Item key="version">Version: {version}</Menu.Item>
<SubMenu
key="download"
onChange={this.handleVisibleChange}
title={
<span>
<span>Download</span>
</span>
}
>
<Menu.Item key="1">Experiment Parameters</Menu.Item>
<Menu.Item key="2">NNImanager Logfile</Menu.Item>
<Menu.Item key="3">Dispatcher Logfile</Menu.Item>
</SubMenu>
</Menu>
);
}
// nav bar <1299
showMenu = () => {
if (this.divMenu !== null) {
this.countOfMenu = this.countOfMenu + 1;
if (this.countOfMenu % 2 === 0) {
this.divMenu.setAttribute('class', 'hide');
} else {
this.divMenu.setAttribute('class', 'show');
}
}
});
}
handleMenuClick = (e: EventPer) => {
if (this._isMounted) { this.setState({ menuVisible: false }); }
// download experiment related content
switch (e.key) {
case '1':
this.downExperimentContent();
break;
case '2':
this.downnnimanagerLog();
break;
case '3':
this.downDispatcherlog();
break;
default:
}
}
handleVisibleChange = (flag: boolean) => {
this.setState({ menuVisible: flag });
}
componentDidMount() {
this._isMounted = true;
this.getNNIversion();
}
componentWillUnmount() {
this._isMounted = false;
}
render() {
const { version, menuVisible } = this.state;
const feed = `https://github.com/Microsoft/nni/issues/new?labels=${version}`;
const menu = (
<Menu onClick={this.handleMenuClick}>
<Menu.Item key="1">Experiment Parameters</Menu.Item>
<Menu.Item key="2">NNImanager Logfile</Menu.Item>
<Menu.Item key="3">Dispatcher Logfile</Menu.Item>
</Menu>
);
return (
<Row className="nav">
<Col span={8}>
<ul className="link">
<li className="logo">
<Link to={'/oview'}>
<img src={require('../static/img/logo2.png')} style={{ width: 88 }} alt="NNI logo" />
</Link>
</li>
<li className="tab firstTab">
<Link to={'/oview'} activeClassName="high">
Overview
</Link>
</li>
<li className="tab">
<Link to={'/detail'} activeClassName="high">
Trials detail
</Link>
</li>
</ul>
</Col>
<Col span={16} className="feedback">
<Dropdown
className="dropdown"
overlay={menu}
onVisibleChange={this.handleVisibleChange}
visible={menuVisible}
>
<a className="ant-dropdown-link" href="#">
Download <Icon type="down" />
</a>
</Dropdown>
<a href={feed} target="_blank">
<img
src={require('../static/img/icon/issue.png')}
alt="NNI github issue"
/>
Feedback
</a>
<span className="version">Version: {version}</span>
</Col>
</Row>
);
}
}
select = () => {
return (
<Select
onSelect={this.getInterval}
defaultValue="Refresh every 10s"
className="interval"
>
<Option value="close">Disable Auto Refresh</Option>
<Option value="10">Refresh every 10s</Option>
<Option value="20">Refresh every 20s</Option>
<Option value="30">Refresh every 30s</Option>
<Option value="60">Refresh every 1min</Option>
</Select>
);
}
fresh = (event: React.SyntheticEvent<EventTarget>) => {
event.preventDefault();
const whichPage = window.location.pathname;
this.props.changeFresh(whichPage);
}
componentDidMount() {
this._isMounted = true;
this.getNNIversion();
}
componentWillUnmount() {
this._isMounted = false;
}
render() {
const { version, menuVisible } = this.state;
const feed = `https://github.com/Microsoft/nni/issues/new?labels=${version}`;
return (
<Row>
<MediaQuery query="(min-width: 1299px)">
<Row className="nav">
<ul className="link">
<li className="logo">
<Link to={'/oview'}>
<img
src={require('../static/img/logo2.png')}
style={{ width: 88 }}
alt="NNI logo"
/>
</Link>
</li>
<li className="tab firstTab">
<Link to={'/oview'} activeClassName="high">
Overview
</Link>
</li>
<li className="tab">
<Link to={'/detail'} activeClassName="high">
Trials detail
</Link>
</li>
<li className="feedback">
<span className="fresh" onClick={this.fresh}>
<Icon type="sync"/><span>Fresh</span>
</span>
<Dropdown
className="dropdown"
overlay={this.menu()}
onVisibleChange={this.handleVisibleChange}
visible={menuVisible}
trigger={['click']}
>
<a className="ant-dropdown-link" href="#">
Download <Icon type="down" />
</a>
</Dropdown>
<a href={feed} target="_blank">
<img
src={require('../static/img/icon/issue.png')}
alt="NNI github issue"
/>
Feedback
</a>
<span className="version">Version: {version}</span>
</li>
</ul>
</Row>
</MediaQuery>
<MediaQuery query="(max-width: 1299px)">
<Row className="little">
<Col span={6} className="menu">
<Icon type="unordered-list" className="more" onClick={this.showMenu} />
<div ref={div => this.divMenu = div} className="hide">{this.navigationBar()}</div>
</Col>
<Col span={10} className="logo">
<Link to={'/oview'}>
<img
src={require('../static/img/logo2.png')}
style={{ width: 88 }}
alt="NNI logo"
/>
</Link>
</Col>
</Row>
</MediaQuery>
{this.select()}
</Row>
);
}
}
export default SlideBar;
\ No newline at end of file
......@@ -27,14 +27,18 @@ interface TrialDetailState {
entriesInSelect: string;
searchSpace: string;
isMultiPhase: boolean;
isTableLoading: boolean;
whichGraph: string;
hyperCounts: number; // user click the hyper-parameter counts
durationCounts: number;
intermediateCounts: number;
}
class TrialsDetail extends React.Component<{}, TrialDetailState> {
interface TrialsDetailProps {
interval: number;
whichPageToFresh: string;
}
class TrialsDetail extends React.Component<TrialsDetailProps, TrialDetailState> {
public _isMounted = false;
public interAccuracy = 0;
......@@ -62,7 +66,7 @@ class TrialsDetail extends React.Component<{}, TrialDetailState> {
</div>
);
constructor(props: {}) {
constructor(props: TrialsDetailProps) {
super(props);
this.state = {
......@@ -79,7 +83,6 @@ class TrialsDetail extends React.Component<{}, TrialDetailState> {
whichGraph: '1',
isHasSearch: false,
isMultiPhase: false,
isTableLoading: false,
hyperCounts: 0,
durationCounts: 0,
intermediateCounts: 0
......@@ -95,9 +98,6 @@ class TrialsDetail extends React.Component<{}, TrialDetailState> {
])
.then(axios.spread((res, res1) => {
if (res.status === 200 && res1.status === 200) {
if (this._isMounted === true) {
this.setState(() => ({ isTableLoading: true }));
}
const trialJobs = res.data;
const metricSource = res1.data;
const trialTable: Array<TableObj> = [];
......@@ -144,7 +144,7 @@ class TrialsDetail extends React.Component<{}, TrialDetailState> {
const items = metricSource[key];
if (items.trialJobId === id) {
// succeed trial, last intermediate result is final result
// final result format may be object
// final result format may be object
if (typeof JSON.parse(items.data) === 'object') {
mediate.push(JSON.parse(items.data).default);
} else {
......@@ -187,10 +187,7 @@ class TrialsDetail extends React.Component<{}, TrialDetailState> {
}
}
if (this._isMounted) {
this.setState(() => ({
isTableLoading: false,
tableListSource: trialTable
}));
this.setState(() => ({ tableListSource: trialTable }));
}
if (entriesInSelect === 'all' && this._isMounted) {
this.setState(() => ({
......@@ -235,21 +232,24 @@ class TrialsDetail extends React.Component<{}, TrialDetailState> {
// close timer
isOffIntervals = () => {
axios(`${MANAGER_IP}/check-status`, {
method: 'GET'
})
.then(res => {
if (res.status === 200 && this._isMounted) {
switch (res.data.status) {
case 'DONE':
case 'ERROR':
case 'STOPPED':
const { interval } = this.props;
if (interval === 0) {
window.clearInterval(this.interTableList);
return;
} else {
axios(`${MANAGER_IP}/check-status`, {
method: 'GET'
})
.then(res => {
if (res.status === 200 && this._isMounted) {
const expStatus = res.data.status;
if (expStatus === 'DONE' || expStatus === 'ERROR' || expStatus === 'STOPPED') {
window.clearInterval(this.interTableList);
break;
default:
return;
}
}
}
});
});
}
}
handleEntriesSelect = (value: string) => {
......@@ -312,11 +312,23 @@ class TrialsDetail extends React.Component<{}, TrialDetailState> {
});
}
componentWillReceiveProps(nextProps: TrialsDetailProps) {
const { interval, whichPageToFresh } = nextProps;
window.clearInterval(this.interTableList);
if (interval !== 0) {
this.interTableList = window.setInterval(this.getDetailSource, interval * 1000);
}
if (whichPageToFresh.includes('/detail')) {
this.getDetailSource();
}
}
componentDidMount() {
this._isMounted = true;
const { interval } = this.props;
this.getDetailSource();
this.interTableList = window.setInterval(this.getDetailSource, 10000);
this.interTableList = window.setInterval(this.getDetailSource, interval * 1000);
this.checkExperimentPlatform();
}
......@@ -330,14 +342,13 @@ class TrialsDetail extends React.Component<{}, TrialDetailState> {
const {
tableListSource, searchResultSource, isHasSearch, isMultiPhase,
entriesTable, experimentPlatform, searchSpace, experimentLogCollection,
whichGraph, isTableLoading
whichGraph
} = this.state;
const source = isHasSearch ? searchResultSource : tableListSource;
return (
<div>
<div className="trial" id="tabsty">
<Tabs type="card" onChange={this.handleWhichTabs}>
{/* <TabPane tab={this.titleOfacc} key="1" destroyInactiveTabPane={true}> */}
<TabPane tab={this.titleOfacc} key="1">
<Row className="graph">
<DefaultPoint
......@@ -358,7 +369,6 @@ class TrialsDetail extends React.Component<{}, TrialDetailState> {
</TabPane>
<TabPane tab={this.titleOfDuration} key="3">
<Duration source={source} whichGraph={whichGraph} />
{/* <Duration source={source} whichGraph={whichGraph} clickCounts={durationCounts} /> */}
</TabPane>
<TabPane tab={this.titleOfIntermediate} key="4">
<Intermediate source={source} whichGraph={whichGraph} />
......@@ -407,7 +417,6 @@ class TrialsDetail extends React.Component<{}, TrialDetailState> {
<TableList
entries={entriesTable}
tableSource={source}
isTableLoading={isTableLoading}
isMultiPhase={isMultiPhase}
platform={experimentPlatform}
updateList={this.getDetailSource}
......
......@@ -78,7 +78,7 @@ class Progressed extends React.Component<ProgressProps, ProgressState> {
}).then(res => {
if (res.status === 200) {
message.destroy();
message.success(`Update ${CONTROLTYPE[1].toLocaleLowerCase()}
message.success(`Update ${CONTROLTYPE[1].toLocaleLowerCase()}
successfully`);
// rerender trial profile message
const { updateFile } = this.props;
......
......@@ -123,7 +123,7 @@ class OpenRow extends React.Component<OpenRowProps, OpenRowState> {
<Button
onClick={this.showFormatModal.bind(this, record)}
>
Copy as python
Copy as json
</Button>
</Row>
</Row>
......
......@@ -15,7 +15,7 @@ class TrialLog extends React.Component<TrialLogProps, {}> {
render() {
const { logStr } = this.props;
return (
<div>
<LogPathChild
......
......@@ -115,13 +115,13 @@ class Intermediate extends React.Component<IntermediateProps, IntermediateState>
},
xAxis: {
type: 'category',
name: 'Scape',
name: 'Step',
boundaryGap: false,
data: xAxis
},
yAxis: {
type: 'value',
name: 'Intermediate'
name: 'metric'
},
series: trialIntermediate
};
......
......@@ -22,6 +22,7 @@ interface ParaState {
max: number; // graph color bar limit
min: number;
sutrialCount: number; // succeed trial numbers for SUC
succeedRenderCount: number; // all succeed trials number
clickCounts: number;
isLoadConfirm: boolean;
}
......@@ -68,6 +69,7 @@ class Para extends React.Component<ParaProps, ParaState> {
min: 0,
max: 1,
sutrialCount: 10000000,
succeedRenderCount: 10000000,
clickCounts: 1,
isLoadConfirm: false
};
......@@ -76,7 +78,8 @@ class Para extends React.Component<ParaProps, ParaState> {
getParallelAxis =
(
dimName: Array<string>, parallelAxis: Array<Dimobj>,
accPara: Array<number>, eachTrialParams: Array<string>
accPara: Array<number>, eachTrialParams: Array<string>,
lengthofTrials: number
) => {
// get data for every lines. if dim is choice type, number -> toString()
const paraYdata: number[][] = [];
......@@ -120,7 +123,7 @@ class Para extends React.Component<ParaProps, ParaState> {
if (swapAxisArr.length >= 2) {
this.swapGraph(paraData, swapAxisArr);
}
this.getOption(paraData);
this.getOption(paraData, lengthofTrials);
if (this._isMounted === true) {
this.setState(() => ({ paraBack: paraData }));
}
......@@ -159,8 +162,8 @@ class Para extends React.Component<ParaProps, ParaState> {
parallelAxis.push({
dim: i,
name: dimName[i],
max: searchKey._value[0] - 1,
min: 0
min: searchKey._value[0],
max: searchKey._value[1],
});
break;
......@@ -248,7 +251,8 @@ class Para extends React.Component<ParaProps, ParaState> {
this.setState({
paraNodata: 'No data',
option: optionOfNull,
sutrialCount: 0
sutrialCount: 0,
succeedRenderCount: 0
});
}
} else {
......@@ -265,7 +269,7 @@ class Para extends React.Component<ParaProps, ParaState> {
});
if (this._isMounted) {
this.setState({ max: Math.max(...accPara), min: Math.min(...accPara) }, () => {
this.getParallelAxis(dimName, parallelAxis, accPara, eachTrialParams);
this.getParallelAxis(dimName, parallelAxis, accPara, eachTrialParams, lenOfDataSource);
});
}
}
......@@ -283,7 +287,7 @@ class Para extends React.Component<ParaProps, ParaState> {
}
// deal with response data into pic data
getOption = (dataObj: ParaObj) => {
getOption = (dataObj: ParaObj, lengthofTrials: number) => {
// dataObj [[y1], [y2]... [default metric]]
const { max, min } = this.state;
const parallelAxis = dataObj.parallelAxis;
......@@ -348,6 +352,7 @@ class Para extends React.Component<ParaProps, ParaState> {
this.setState(() => ({
option: optionown,
paraNodata: '',
succeedRenderCount: lengthofTrials,
sutrialCount: paralleData.length
}));
}
......@@ -367,7 +372,7 @@ class Para extends React.Component<ParaProps, ParaState> {
}
swapReInit = () => {
const { clickCounts } = this.state;
const { clickCounts, succeedRenderCount } = this.state;
const val = clickCounts + 1;
if (this._isMounted) {
this.setState({ isLoadConfirm: true, clickCounts: val, });
......@@ -419,7 +424,7 @@ class Para extends React.Component<ParaProps, ParaState> {
paraData[paraItem][dim1] = paraData[paraItem][dim2];
paraData[paraItem][dim2] = temp;
});
this.getOption(paraBack);
this.getOption(paraBack, succeedRenderCount);
// please wait the data
if (this._isMounted) {
this.setState(() => ({
......@@ -503,12 +508,16 @@ class Para extends React.Component<ParaProps, ParaState> {
return true;
}
const { sutrialCount, clickCounts } = nextState;
const { sutrialCount, clickCounts, succeedRenderCount } = nextState;
const beforeCount = this.state.sutrialCount;
const beforeClickCount = this.state.clickCounts;
const beforeRealRenderCount = this.state.succeedRenderCount;
if (sutrialCount !== beforeCount) {
return true;
}
if (succeedRenderCount !== beforeRealRenderCount) {
return true;
}
if (clickCounts !== beforeClickCount) {
return true;
......
......@@ -30,7 +30,6 @@ interface TableListProps {
platform: string;
logCollection: boolean;
isMultiPhase: boolean;
isTableLoading: boolean;
}
interface TableListState {
......@@ -195,7 +194,7 @@ class TableList extends React.Component<TableListProps, TableListState> {
render() {
const { entries, tableSource, updateList, isTableLoading } = this.props;
const { entries, tableSource, updateList } = this.props;
const { intermediateOption, modalVisible, isShowColumn, columnSelected } = this.state;
let showTitle = COLUMN;
let bgColor = '';
......@@ -264,8 +263,12 @@ class TableList extends React.Component<TableListProps, TableListState> {
sorter: (a: TableObj, b: TableObj) => (a.duration as number) - (b.duration as number),
render: (text: string, record: TableObj) => {
let duration;
if (record.duration !== undefined && record.duration > 0) {
duration = convertDuration(record.duration);
if (record.duration !== undefined) {
if (record.duration > 0 && record.duration < 1) {
duration = `${record.duration}s`;
} else {
duration = convertDuration(record.duration);
}
} else {
duration = 0;
}
......@@ -418,7 +421,6 @@ class TableList extends React.Component<TableListProps, TableListState> {
dataSource={tableSource}
className="commonTableStyle"
pagination={{ pageSize: entries }}
loading={isTableLoading}
/>
{/* Intermediate Result Modal */}
<Modal
......
......@@ -49,8 +49,8 @@ table {
border-collapse: collapse;
border-spacing: 0;
}
@font-face {
font-family: 'Segoe';
@font-face {
font-family: 'Segoe';
src: url('./static/font/SegoePro-Regular.ttf');
}
}
......@@ -50,7 +50,7 @@ const getFinalResult = (final: Array<FinalResult>) => {
}
};
// get final result value // acc obj
// get final result value // acc obj
const getFinal = (final: Array<FinalResult>) => {
let showDefault: FinalType;
if (final) {
......
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