Unverified Commit 403195f0 authored by Yuge Zhang's avatar Yuge Zhang Committed by GitHub
Browse files

Merge branch 'master' into nn-meter

parents 99aa8226 a7278d2d
...@@ -100,8 +100,8 @@ describe('Unit Test for LocalTrainingService', () => { ...@@ -100,8 +100,8 @@ describe('Unit Test for LocalTrainingService', () => {
fs.mkdirSync(jobDetail.workingDirectory) fs.mkdirSync(jobDetail.workingDirectory)
fs.writeFileSync(path.join(jobDetail.workingDirectory, 'trial.log'), 'trial log') fs.writeFileSync(path.join(jobDetail.workingDirectory, 'trial.log'), 'trial log')
fs.writeFileSync(path.join(jobDetail.workingDirectory, 'stderr'), 'trial stderr') fs.writeFileSync(path.join(jobDetail.workingDirectory, 'stderr'), 'trial stderr')
chai.expect(await localTrainingService.getTrialLog(jobDetail.id, 'TRIAL_LOG')).to.be.equals('trial log'); chai.expect(await localTrainingService.getTrialFile(jobDetail.id, 'trial.log')).to.be.equals('trial log');
chai.expect(await localTrainingService.getTrialLog(jobDetail.id, 'TRIAL_ERROR')).to.be.equals('trial stderr'); chai.expect(await localTrainingService.getTrialFile(jobDetail.id, 'stderr')).to.be.equals('trial stderr');
fs.unlinkSync(path.join(jobDetail.workingDirectory, 'trial.log')) fs.unlinkSync(path.join(jobDetail.workingDirectory, 'trial.log'))
fs.unlinkSync(path.join(jobDetail.workingDirectory, 'stderr')) fs.unlinkSync(path.join(jobDetail.workingDirectory, 'stderr'))
fs.rmdirSync(jobDetail.workingDirectory) fs.rmdirSync(jobDetail.workingDirectory)
......
...@@ -503,6 +503,13 @@ ...@@ -503,6 +503,13 @@
"@types/minimatch" "*" "@types/minimatch" "*"
"@types/node" "*" "@types/node" "*"
"@types/http-proxy@^1.17.7":
version "1.17.7"
resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.7.tgz#30ea85cc2c868368352a37f0d0d3581e24834c6f"
integrity sha512-9hdj6iXH64tHSLTY+Vt2eYOGzSogC+JQ2H7bdPWkuh7KXP5qLllWx++t+K9Wk556c3dkDdPws/SpMRi0sdCT1w==
dependencies:
"@types/node" "*"
"@types/js-base64@^3.3.1": "@types/js-base64@^3.3.1":
version "3.3.1" version "3.3.1"
resolved "https://registry.yarnpkg.com/@types/js-base64/-/js-base64-3.3.1.tgz#36c2d6dc126277ea28a4d0599d0cafbf547b51e6" resolved "https://registry.yarnpkg.com/@types/js-base64/-/js-base64-3.3.1.tgz#36c2d6dc126277ea28a4d0599d0cafbf547b51e6"
...@@ -2071,6 +2078,11 @@ etag@~1.8.1: ...@@ -2071,6 +2078,11 @@ etag@~1.8.1:
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
eventemitter3@^4.0.0:
version "4.0.7"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
execa@^0.7.0: execa@^0.7.0:
version "0.7.0" version "0.7.0"
resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777"
...@@ -2273,6 +2285,11 @@ flatted@^3.1.0: ...@@ -2273,6 +2285,11 @@ flatted@^3.1.0:
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.1.1.tgz#c4b489e80096d9df1dfc97c79871aea7c617c469" resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.1.1.tgz#c4b489e80096d9df1dfc97c79871aea7c617c469"
integrity sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA== integrity sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==
follow-redirects@^1.0.0:
version "1.14.1"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.1.tgz#d9114ded0a1cfdd334e164e6662ad02bfd91ff43"
integrity sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==
for-in@^0.1.3: for-in@^0.1.3:
version "0.1.8" version "0.1.8"
resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.8.tgz#d8773908e31256109952b1fdb9b3fa867d2775e1" resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.8.tgz#d8773908e31256109952b1fdb9b3fa867d2775e1"
...@@ -2715,6 +2732,15 @@ http-proxy-agent@^4.0.1: ...@@ -2715,6 +2732,15 @@ http-proxy-agent@^4.0.1:
agent-base "6" agent-base "6"
debug "4" debug "4"
http-proxy@^1.18.1:
version "1.18.1"
resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549"
integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==
dependencies:
eventemitter3 "^4.0.0"
follow-redirects "^1.0.0"
requires-port "^1.0.0"
http-signature@~1.2.0: http-signature@~1.2.0:
version "1.2.0" version "1.2.0"
resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
...@@ -5010,6 +5036,11 @@ require-main-filename@^2.0.0: ...@@ -5010,6 +5036,11 @@ require-main-filename@^2.0.0:
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==
requires-port@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
resolve-from@^4.0.0: resolve-from@^4.0.0:
version "4.0.0" version "4.0.0"
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
......
...@@ -56,8 +56,13 @@ class OpenRow extends React.Component<OpenRowProps, OpenRowState> { ...@@ -56,8 +56,13 @@ class OpenRow extends React.Component<OpenRowProps, OpenRowState> {
} }
}; };
openTrialLog = (type: string): void => { openTrialLog = (filename: string): void => {
window.open(`${MANAGER_IP}/trial-log/${this.props.trialId}/${type}`); window.open(`${MANAGER_IP}/trial-file/${this.props.trialId}/${filename}`);
};
openModelOnnx = (): void => {
// TODO: netron might need prefix.
window.open(`/netron/index.html?url=${MANAGER_IP}/trial-file/${this.props.trialId}/model.onnx`);
}; };
render(): React.ReactNode { render(): React.ReactNode {
...@@ -113,16 +118,16 @@ class OpenRow extends React.Component<OpenRowProps, OpenRowState> { ...@@ -113,16 +118,16 @@ class OpenRow extends React.Component<OpenRowProps, OpenRowState> {
<div id='trialog'> <div id='trialog'>
<div className='copy' style={{ marginTop: 15 }}> <div className='copy' style={{ marginTop: 15 }}>
<PrimaryButton <PrimaryButton
onClick={this.openTrialLog.bind(this, 'TRIAL_LOG')} onClick={this.openTrialLog.bind(this, 'trial.log')}
text='View trial log' text='View trial log'
/> />
<PrimaryButton <PrimaryButton
onClick={this.openTrialLog.bind(this, 'TRIAL_ERROR')} onClick={this.openTrialLog.bind(this, 'stderr')}
text='View trial error' text='View trial error'
styles={{ root: { marginLeft: 15 } }} styles={{ root: { marginLeft: 15 } }}
/> />
<PrimaryButton <PrimaryButton
onClick={this.openTrialLog.bind(this, 'TRIAL_STDOUT')} onClick={this.openTrialLog.bind(this, 'stdout')}
text='View trial stdout' text='View trial stdout'
styles={{ root: { marginLeft: 15 } }} styles={{ root: { marginLeft: 15 } }}
/> />
...@@ -132,6 +137,18 @@ class OpenRow extends React.Component<OpenRowProps, OpenRowState> { ...@@ -132,6 +137,18 @@ class OpenRow extends React.Component<OpenRowProps, OpenRowState> {
) )
} }
</PivotItem> </PivotItem>
{EXPERIMENT.metadata.tag.includes('retiarii') ? (
<PivotItem headerText='Visualization' key='3' itemIcon='FlowChart'>
<div id='visualization'>
<div id='visualizationText'>Visualize models with 3rd-party tools.</div>
<PrimaryButton
onClick={this.openModelOnnx.bind(this)}
text='Netron'
styles={{ root: { marginLeft: 15 } }}
/>
</div>
</PivotItem>
) : null}
</Pivot> </Pivot>
</Stack> </Stack>
</Stack> </Stack>
......
...@@ -154,75 +154,89 @@ function Search(props): any { ...@@ -154,75 +154,89 @@ function Search(props): any {
} }
allFilterConditions.forEach(eachFilterConditionStr => { allFilterConditions.forEach(eachFilterConditionStr => {
let eachFilterConditionArr: string[] = []; // input content looks like that: `Trial id:`
if (
// EXPERIMENT.searchSpace[parameter]._type === 'choice' eachFilterConditionStr.endsWith(':') ||
if (eachFilterConditionStr.includes('>' || '<')) { eachFilterConditionStr.endsWith('<') ||
const operator = eachFilterConditionStr.includes('>') === true ? '>' : '<'; eachFilterConditionStr.endsWith('>') ||
eachFilterConditionArr = eachFilterConditionStr.trim().split(operator); eachFilterConditionStr.endsWith('')
newSearchFilter.push({ ) {
name: eachFilterConditionArr[0], return;
operator: operator,
value1: eachFilterConditionArr[1],
value2: '',
choice: [],
isChoice: false
});
} else if (eachFilterConditionStr.includes('')) {
// drop_rate≠6; status≠[x,xx,xxx]; conv_size≠[3,7]
eachFilterConditionArr = eachFilterConditionStr.trim().split('');
const filterName = eachFilterConditionArr[0] === 'Status' ? 'StatusNNI' : eachFilterConditionArr[0];
const isChoicesType = isChoiceType(filterName);
newSearchFilter.push({
name: filterName,
operator: '',
value1: isChoicesType ? '' : JSON.parse(eachFilterConditionArr[1]),
value2: '',
choice: isChoicesType ? convertStringArrToList(eachFilterConditionArr[1]) : [],
isChoice: isChoicesType ? true : false
});
} else { } else {
// = : conv_size:[1,2,3,4]; Trial id:3; hidden_size:[1,2], status:[val1,val2,val3] let eachFilterConditionArr: string[] = [];
eachFilterConditionArr = eachFilterConditionStr.trim().split(':');
const filterName = eachFilterConditionArr[0] === 'Status' ? 'StatusNNI' : eachFilterConditionArr[0]; // EXPERIMENT.searchSpace[parameter]._type === 'choice'
const isChoicesType = isChoiceType(filterName); if (eachFilterConditionStr.includes('>' || '<')) {
const isArray = const operator = eachFilterConditionStr.includes('>') === true ? '>' : '<';
eachFilterConditionArr.length > 1 && eachFilterConditionArr[1].includes('[' || ']') ? true : false; eachFilterConditionArr = eachFilterConditionStr.trim().split(operator);
if (isArray === true) { newSearchFilter.push({
if (isChoicesType === true) { name: eachFilterConditionArr[0],
// status:[SUCCEEDED] operator: operator,
newSearchFilter.push({ value1: eachFilterConditionArr[1],
name: filterName, value2: '',
operator: '=', choice: [],
value1: '', isChoice: false
value2: '', });
choice: convertStringArrToList(eachFilterConditionArr[1]), } else if (eachFilterConditionStr.includes('')) {
isChoice: true // drop_rate≠6; status≠[x,xx,xxx]; conv_size≠[3,7]
}); eachFilterConditionArr = eachFilterConditionStr.trim().split('');
const filterName = eachFilterConditionArr[0] === 'Status' ? 'StatusNNI' : eachFilterConditionArr[0];
const isChoicesType = isChoiceType(filterName);
newSearchFilter.push({
name: filterName,
operator: '',
value1: isChoicesType ? '' : JSON.parse(eachFilterConditionArr[1]),
value2: '',
choice: isChoicesType ? convertStringArrToList(eachFilterConditionArr[1]) : [],
isChoice: isChoicesType ? true : false
});
} else if (eachFilterConditionStr.includes(':')) {
// = : conv_size:[1,2,3,4]; Trial id:3; hidden_size:[1,2], status:[val1,val2,val3]
eachFilterConditionArr = eachFilterConditionStr.trim().split(':');
const filterName = eachFilterConditionArr[0] === 'Status' ? 'StatusNNI' : eachFilterConditionArr[0];
const isChoicesType = isChoiceType(filterName);
const isArray =
eachFilterConditionArr.length > 1 && eachFilterConditionArr[1].includes('[' || ']')
? true
: false;
if (isArray === true) {
if (isChoicesType === true) {
// status:[SUCCEEDED]
newSearchFilter.push({
name: filterName,
operator: '=',
value1: '',
value2: '',
choice: convertStringArrToList(eachFilterConditionArr[1]),
isChoice: true
});
} else {
// drop_rate:[1,10]
newSearchFilter.push({
name: eachFilterConditionArr[0],
operator: 'between',
value1: JSON.parse(eachFilterConditionArr[1])[0],
value2: JSON.parse(eachFilterConditionArr[1])[1],
choice: [],
isChoice: false
});
}
} else { } else {
// drop_rate:[1,10]
newSearchFilter.push({ newSearchFilter.push({
name: eachFilterConditionArr[0], name: eachFilterConditionArr[0],
operator: 'between', operator: '=',
value1: JSON.parse(eachFilterConditionArr[1])[0], value1: eachFilterConditionArr[1],
value2: JSON.parse(eachFilterConditionArr[1])[1], value2: '',
choice: [], choice: [],
isChoice: false isChoice: false
}); });
} }
} else { } else {
newSearchFilter.push({ // user input: Trial id
name: eachFilterConditionArr[0], return;
operator: '=',
value1: eachFilterConditionArr[1],
value2: '',
choice: [],
isChoice: false
});
} }
} }
}); });
changeTableListPage(newSearchFilter); changeTableListPage(newSearchFilter);
} }
......
...@@ -165,6 +165,21 @@ interface ExperimentProfile { ...@@ -165,6 +165,21 @@ interface ExperimentProfile {
revision: number; revision: number;
} }
interface ExperimentMetadata {
id: string;
port: number;
startTime: number | string;
endTime: number | string;
status: string;
platform: string;
experimentName: string;
tag: any[];
pid: number;
webuiUrl: any[];
logDir: string;
prefixUrl: string | null;
}
interface NNIManagerStatus { interface NNIManagerStatus {
status: string; status: string;
errors: string[]; errors: string[];
...@@ -230,6 +245,7 @@ export { ...@@ -230,6 +245,7 @@ export {
MetricDataRecord, MetricDataRecord,
TrialJobInfo, TrialJobInfo,
ExperimentProfile, ExperimentProfile,
ExperimentMetadata,
NNIManagerStatus, NNIManagerStatus,
EventMap, EventMap,
SingleAxis, SingleAxis,
......
import { MANAGER_IP } from '../const'; import { MANAGER_IP } from '../const';
import { ExperimentConfig, toSeconds } from '../experimentConfig'; import { ExperimentConfig, toSeconds } from '../experimentConfig';
import { ExperimentProfile, NNIManagerStatus } from '../interface'; import { ExperimentProfile, ExperimentMetadata, NNIManagerStatus } from '../interface';
import { requestAxios } from '../function'; import { requestAxios } from '../function';
import { SearchSpace } from './searchspace'; import { SearchSpace } from './searchspace';
...@@ -32,8 +32,24 @@ const emptyProfile: ExperimentProfile = { ...@@ -32,8 +32,24 @@ const emptyProfile: ExperimentProfile = {
revision: 0 revision: 0
}; };
const emptyMetadata: ExperimentMetadata = {
id: '',
port: 0,
startTime: '',
endTime: '',
status: '',
platform: '',
experimentName: '',
tag: [],
pid: 0,
webuiUrl: [],
logDir: '',
prefixUrl: null
};
class Experiment { class Experiment {
private profileField?: ExperimentProfile; private profileField?: ExperimentProfile;
private metadataField?: ExperimentMetadata = undefined;
private statusField?: NNIManagerStatus = undefined; private statusField?: NNIManagerStatus = undefined;
private isNestedExperiment: boolean = false; private isNestedExperiment: boolean = false;
private isexperimentError: boolean = false; private isexperimentError: boolean = false;
...@@ -82,10 +98,14 @@ class Experiment { ...@@ -82,10 +98,14 @@ class Experiment {
public async update(): Promise<boolean> { public async update(): Promise<boolean> {
let updated = false; let updated = false;
await requestAxios(`${MANAGER_IP}/experiment`) await Promise.all([requestAxios(`${MANAGER_IP}/experiment`), requestAxios(`${MANAGER_IP}/experiment-metadata`)])
.then(data => { .then(([profile, metadata]) => {
updated = updated || !compareProfiles(this.profileField, data); updated ||= !compareProfiles(this.profileField, profile);
this.profileField = data; this.profileField = profile;
if (JSON.stringify(this.metadataField) !== JSON.stringify(metadata)) {
this.metadataField = metadata;
}
}) })
.catch(error => { .catch(error => {
this.isexperimentError = true; this.isexperimentError = true;
...@@ -111,6 +131,10 @@ class Experiment { ...@@ -111,6 +131,10 @@ class Experiment {
return this.profileField === undefined ? emptyProfile : this.profileField; return this.profileField === undefined ? emptyProfile : this.profileField;
} }
get metadata(): ExperimentMetadata {
return this.metadataField === undefined ? emptyMetadata : this.metadataField;
}
get config(): ExperimentConfig { get config(): ExperimentConfig {
return this.profile.params; return this.profile.params;
} }
......
...@@ -55,3 +55,8 @@ $bgColor: #f2f2f2; ...@@ -55,3 +55,8 @@ $bgColor: #f2f2f2;
} }
} }
} }
#visualizationText {
margin: 5px 0 10px 15px;
font-size: 14px;
}
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