Unverified Commit 3d221da9 authored by fishyds's avatar fishyds Committed by GitHub
Browse files

Merge latest code changes into Github Master (#54)

* Merge latest code changes into Github Master

* temporary modification for travis

* temporary modification for travis
parent c015421c
...@@ -28,16 +28,19 @@ import { NNIError, NNIErrorNames } from '../common/errors'; ...@@ -28,16 +28,19 @@ import { NNIError, NNIErrorNames } from '../common/errors';
import { isNewExperiment } from '../common/experimentStartupInfo'; import { isNewExperiment } from '../common/experimentStartupInfo';
import { getLogger, Logger } from '../common/log'; import { getLogger, Logger } from '../common/log';
import { ExperimentProfile, Manager, TrialJobStatistics} from '../common/manager'; import { ExperimentProfile, Manager, TrialJobStatistics} from '../common/manager';
import { RestServer } from './server'; import { ValidationSchemas } from './restValidationSchemas';
import { NNIRestServer } from './nniRestServer';
import { TensorBoard } from './tensorboard'; import { TensorBoard } from './tensorboard';
const expressJoi = require('express-joi-validator');
class NNIRestHandler { class NNIRestHandler {
private restServer: RestServer; private restServer: NNIRestServer;
private nniManager: Manager; private nniManager: Manager;
private tb: TensorBoard; private tb: TensorBoard;
private log: Logger; private log: Logger;
constructor(rs: RestServer) { constructor(rs: NNIRestServer) {
this.nniManager = component.get(Manager); this.nniManager = component.get(Manager);
this.restServer = rs; this.restServer = rs;
this.tb = new TensorBoard(); this.tb = new TensorBoard();
...@@ -75,6 +78,15 @@ class NNIRestHandler { ...@@ -75,6 +78,15 @@ class NNIRestHandler {
this.startTensorBoard(router); this.startTensorBoard(router);
this.stopTensorBoard(router); this.stopTensorBoard(router);
// Express-joi-validator configuration
router.use((err: any, req: Request, res: Response, next: any) => {
if (err.isBoom) {
this.log.error(err.output.payload);
return res.status(err.output.statusCode).json(err.output.payload);
}
});
return router; return router;
} }
...@@ -96,7 +108,7 @@ class NNIRestHandler { ...@@ -96,7 +108,7 @@ class NNIRestHandler {
router.get('/check-status', (req: Request, res: Response) => { router.get('/check-status', (req: Request, res: Response) => {
const ds: DataStore = component.get<DataStore>(DataStore); const ds: DataStore = component.get<DataStore>(DataStore);
ds.init().then(() => { ds.init().then(() => {
res.send(); res.send(this.nniManager.getStatus());
}).catch(async (err: Error) => { }).catch(async (err: Error) => {
this.handle_error(err, res); this.handle_error(err, res);
this.log.error(err.message); this.log.error(err.message);
...@@ -117,7 +129,7 @@ class NNIRestHandler { ...@@ -117,7 +129,7 @@ class NNIRestHandler {
} }
private updateExperimentProfile(router: Router): void { private updateExperimentProfile(router: Router): void {
router.put('/experiment', (req: Request, res: Response) => { router.put('/experiment', expressJoi(ValidationSchemas.UPDATEEXPERIMENT), (req: Request, res: Response) => {
this.nniManager.updateExperimentProfile(req.body, req.query.update_type).then(() => { this.nniManager.updateExperimentProfile(req.body, req.query.update_type).then(() => {
res.send(); res.send();
}).catch((err: Error) => { }).catch((err: Error) => {
...@@ -127,7 +139,7 @@ class NNIRestHandler { ...@@ -127,7 +139,7 @@ class NNIRestHandler {
} }
private startExperiment(router: Router): void { private startExperiment(router: Router): void {
router.post('/experiment', (req: Request, res: Response) => { router.post('/experiment', expressJoi(ValidationSchemas.STARTEXPERIMENT), (req: Request, res: Response) => {
if (isNewExperiment()) { if (isNewExperiment()) {
this.nniManager.startExperiment(req.body).then((eid: string) => { this.nniManager.startExperiment(req.body).then((eid: string) => {
res.send({ res.send({
...@@ -171,7 +183,9 @@ class NNIRestHandler { ...@@ -171,7 +183,9 @@ class NNIRestHandler {
} }
private setClusterMetaData(router: Router): void { private setClusterMetaData(router: Router): void {
router.put('/experiment/cluster-metadata', async (req: Request, res: Response) => { router.put(
'/experiment/cluster-metadata', expressJoi(ValidationSchemas.SETCLUSTERMETADATA),
async (req: Request, res: Response) => {
// tslint:disable-next-line:no-any // tslint:disable-next-line:no-any
const metadata: any = req.body; const metadata: any = req.body;
const keys: string[] = Object.keys(metadata); const keys: string[] = Object.keys(metadata);
...@@ -241,7 +255,7 @@ class NNIRestHandler { ...@@ -241,7 +255,7 @@ class NNIRestHandler {
} }
private startTensorBoard(router: Router): void { private startTensorBoard(router: Router): void {
router.post('/tensorboard', async (req: Request, res: Response) => { router.post('/tensorboard', expressJoi(ValidationSchemas.STARTTENSORBOARD), async (req: Request, res: Response) => {
const jobIds: string[] = req.query.job_ids.split(','); const jobIds: string[] = req.query.job_ids.split(',');
const tensorboardCmd: string | undefined = req.query.tensorboard_cmd; const tensorboardCmd: string | undefined = req.query.tensorboard_cmd;
this.tb.startTensorBoard(jobIds, tensorboardCmd).then((endPoint: string) => { this.tb.startTensorBoard(jobIds, tensorboardCmd).then((endPoint: string) => {
...@@ -253,7 +267,7 @@ class NNIRestHandler { ...@@ -253,7 +267,7 @@ class NNIRestHandler {
} }
private stopTensorBoard(router: Router): void { private stopTensorBoard(router: Router): void {
router.delete('/tensorboard', async (req: Request, res: Response) => { router.delete('/tensorboard', expressJoi(ValidationSchemas.STOPTENSORBOARD), async (req: Request, res: Response) => {
const endPoint: string = req.query.endpoint; const endPoint: string = req.query.endpoint;
this.tb.stopTensorBoard(endPoint).then(() => { this.tb.stopTensorBoard(endPoint).then(() => {
res.send(); res.send();
...@@ -285,7 +299,7 @@ class NNIRestHandler { ...@@ -285,7 +299,7 @@ class NNIRestHandler {
} }
} }
export function createRestHandler(rs: RestServer): Router { export function createRestHandler(rs: NNIRestServer): Router {
const handler: NNIRestHandler = new NNIRestHandler(rs); const handler: NNIRestHandler = new NNIRestHandler(rs);
return handler.createRestHandler(); return handler.createRestHandler();
......
/**
* Copyright (c) Microsoft Corporation
* All rights reserved.
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
* to permit persons to whom the Software is furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
'use strict';
const joi = require('joi');
export namespace ValidationSchemas {
export const SETCLUSTERMETADATA = {
body: {
machine_list: joi.array().items(joi.object({
username: joi.string().required(),
ip: joi.string().ip().required(),
port: joi.number().min(1).max(65535).required(),
passwd: joi.string().required(),
sshKeyPath: joi.string(),
passphrase: joi.string()
})),
trial_config: joi.object({
gpuNum: joi.number().min(0).required(),
codeDir: joi.string().min(1).required(),
command: joi.string().min(1).required()
})
}
};
export const STARTEXPERIMENT = {
body: {
experimentName: joi.string().required(),
authorName: joi.string(),
maxTrialNum: joi.number().min(0).required(),
trialConcurrency: joi.number().min(0).required(),
searchSpace: joi.string().required(),
maxExecDuration: joi.number().min(0).required(),
tuner: joi.object({
builtinTunerName: joi.string().valid('TPE', 'Random', 'Anneal', 'Evolution'),
codeDir: joi.string(),
classFileName: joi.string(),
className: joi.string(),
classArgs: joi.any(),
gpuNum: joi.number().min(0),
checkpointDir: joi.string()
}).required(),
assessor: joi.object({
builtinAssessorName: joi.string().valid('Medianstop'),
codeDir: joi.string(),
classFileName: joi.string(),
className: joi.string(),
classArgs: joi.any(),
gpuNum: joi.number().min(0),
checkpointDir: joi.string()
}),
clusterMetaData: joi.array().items(joi.object({
key: joi.string(),
value: joi.any()
}))
}
};
export const UPDATEEXPERIMENT = {
query: {
update_type: joi.string().required().valid('TRIAL_CONCURRENCY', 'MAX_EXEC_DURATION', 'SEARCH_SPACE')
},
body: {
id: joi.string().required(),
revision: joi.number().min(0).required(),
params: joi.object(STARTEXPERIMENT.body).required(),
execDuration: joi.number().required(),
startTime: joi.number(),
endTime: joi.number()
}
};
export const STARTTENSORBOARD = {
query: {
job_ids: joi.string().min(5).max(5).required()
}
};
export const STOPTENSORBOARD = {
query: {
endpoint: joi.string().uri().required()
}
};
}
...@@ -26,7 +26,7 @@ import { MetricDataRecord, MetricType, TrialJobInfo } from '../../common/datasto ...@@ -26,7 +26,7 @@ import { MetricDataRecord, MetricType, TrialJobInfo } from '../../common/datasto
import { MethodNotImplementedError } from '../../common/errors'; import { MethodNotImplementedError } from '../../common/errors';
import { import {
ExperimentParams, ExperimentProfile, Manager, ProfileUpdateType, ExperimentParams, ExperimentProfile, Manager, ProfileUpdateType,
TrialJobStatistics TrialJobStatistics, NNIManagerStatus
} from '../../common/manager'; } from '../../common/manager';
import { import {
TrialJobApplicationForm, TrialJobDetail, TrialJobStatus TrialJobApplicationForm, TrialJobDetail, TrialJobStatus
...@@ -37,6 +37,12 @@ export const testManagerProvider: Provider = { ...@@ -37,6 +37,12 @@ export const testManagerProvider: Provider = {
}; };
export class MockedNNIManager extends Manager { export class MockedNNIManager extends Manager {
public getStatus(): NNIManagerStatus {
return {
status: 'EXPERIMENT_RUNNING',
errors: []
}
}
public updateExperimentProfile(experimentProfile: ExperimentProfile, updateType: ProfileUpdateType): Promise<void> { public updateExperimentProfile(experimentProfile: ExperimentProfile, updateType: ProfileUpdateType): Promise<void> {
return Promise.resolve(); return Promise.resolve();
} }
...@@ -65,9 +71,9 @@ export class MockedNNIManager extends Manager { ...@@ -65,9 +71,9 @@ export class MockedNNIManager extends Manager {
const jobDetail: TrialJobDetail = { const jobDetail: TrialJobDetail = {
id: '1234', id: '1234',
status: 'RUNNING', status: 'RUNNING',
submitTime: new Date(), submitTime: Date.now(),
startTime: new Date(), startTime: Date.now(),
endTime: new Date(), endTime: Date.now(),
tags: ['test'], tags: ['test'],
// tslint:disable-next-line:no-http-string // tslint:disable-next-line:no-http-string
url: 'http://test', url: 'http://test',
...@@ -108,8 +114,8 @@ export class MockedNNIManager extends Manager { ...@@ -108,8 +114,8 @@ export class MockedNNIManager extends Manager {
const jobInfo: TrialJobInfo = { const jobInfo: TrialJobInfo = {
id: '1234', id: '1234',
status: 'SUCCEEDED', status: 'SUCCEEDED',
startTime: new Date(), startTime: Date.now(),
endTime: new Date() endTime: Date.now()
}; };
deferred.resolve(jobInfo); deferred.resolve(jobInfo);
...@@ -137,8 +143,8 @@ export class MockedNNIManager extends Manager { ...@@ -137,8 +143,8 @@ export class MockedNNIManager extends Manager {
}, },
id: '2345', id: '2345',
execDuration: 0, execDuration: 0,
startTime: new Date(), startTime: Date.now(),
endTime: new Date(), endTime: Date.now(),
revision: 0 revision: 0
}; };
...@@ -148,15 +154,15 @@ export class MockedNNIManager extends Manager { ...@@ -148,15 +154,15 @@ export class MockedNNIManager extends Manager {
const job1: TrialJobInfo = { const job1: TrialJobInfo = {
id: '1234', id: '1234',
status: 'SUCCEEDED', status: 'SUCCEEDED',
startTime: new Date(), startTime: Date.now(),
endTime: new Date(), endTime: Date.now(),
finalMetricData: 'lr: 0.01, val accuracy: 0.89, batch size: 256' finalMetricData: 'lr: 0.01, val accuracy: 0.89, batch size: 256'
}; };
const job2: TrialJobInfo = { const job2: TrialJobInfo = {
id: '3456', id: '3456',
status: 'FAILED', status: 'FAILED',
startTime: new Date(), startTime: Date.now(),
endTime: new Date(), endTime: Date.now(),
finalMetricData: '' finalMetricData: ''
}; };
......
...@@ -32,7 +32,7 @@ import { TrainingService } from '../../common/trainingService'; ...@@ -32,7 +32,7 @@ import { TrainingService } from '../../common/trainingService';
import { cleanupUnitTest, prepareUnitTest } from '../../common/utils'; import { cleanupUnitTest, prepareUnitTest } from '../../common/utils';
import { MockedDataStore } from '../../core/test/mockedDatastore'; import { MockedDataStore } from '../../core/test/mockedDatastore';
import { MockedTrainingService } from '../../core/test/mockedTrainingService'; import { MockedTrainingService } from '../../core/test/mockedTrainingService';
import { RestServer } from '../server'; import { NNIRestServer } from '../nniRestServer';
import { testManagerProvider } from './mockedNNIManager'; import { testManagerProvider } from './mockedNNIManager';
describe('Unit test for rest server', () => { describe('Unit test for rest server', () => {
...@@ -44,7 +44,7 @@ describe('Unit test for rest server', () => { ...@@ -44,7 +44,7 @@ describe('Unit test for rest server', () => {
Container.bind(Manager).provider(testManagerProvider); Container.bind(Manager).provider(testManagerProvider);
Container.bind(DataStore).to(MockedDataStore); Container.bind(DataStore).to(MockedDataStore);
Container.bind(TrainingService).to(MockedTrainingService); Container.bind(TrainingService).to(MockedTrainingService);
const restServer: RestServer = component.get(RestServer); const restServer: NNIRestServer = component.get(NNIRestServer);
restServer.start().then(() => { restServer.start().then(() => {
ROOT_URL = `${restServer.endPoint}/api/v1/nni`; ROOT_URL = `${restServer.endPoint}/api/v1/nni`;
done(); done();
...@@ -54,7 +54,7 @@ describe('Unit test for rest server', () => { ...@@ -54,7 +54,7 @@ describe('Unit test for rest server', () => {
}); });
after(() => { after(() => {
component.get<RestServer>(RestServer).stop(); component.get<NNIRestServer>(NNIRestServer).stop();
cleanupUnitTest(); cleanupUnitTest();
}); });
......
...@@ -65,16 +65,16 @@ function decodeCommand(data: Buffer): [boolean, string, string, Buffer] { ...@@ -65,16 +65,16 @@ function decodeCommand(data: Buffer): [boolean, string, string, Buffer] {
class LocalTrialJobDetail implements TrialJobDetail { class LocalTrialJobDetail implements TrialJobDetail {
public id: string; public id: string;
public status: TrialJobStatus; public status: TrialJobStatus;
public submitTime: Date; public submitTime: number;
public startTime?: Date; public startTime?: number;
public endTime?: Date; public endTime?: number;
public tags?: string[]; public tags?: string[];
public url?: string; public url?: string;
public workingDirectory: string; public workingDirectory: string;
public form: JobApplicationForm; public form: JobApplicationForm;
public pid?: number; public pid?: number;
constructor(id: string, status: TrialJobStatus, submitTime: Date, workingDirectory: string, form: JobApplicationForm) { constructor(id: string, status: TrialJobStatus, submitTime: number, workingDirectory: string, form: JobApplicationForm) {
this.id = id; this.id = id;
this.status = status; this.status = status;
this.submitTime = submitTime; this.submitTime = submitTime;
...@@ -152,7 +152,7 @@ class LocalTrainingService implements TrainingService { ...@@ -152,7 +152,7 @@ class LocalTrainingService implements TrainingService {
} }
if (!alive) { if (!alive) {
trialJob.endTime = new Date(); trialJob.endTime = Date.now();
this.setTrialJobStatus(trialJob, 'FAILED'); this.setTrialJobStatus(trialJob, 'FAILED');
try { try {
const state: string = await fs.promises.readFile(path.join(trialJob.workingDirectory, '.nni', 'state'), 'utf8'); const state: string = await fs.promises.readFile(path.join(trialJob.workingDirectory, '.nni', 'state'), 'utf8');
...@@ -162,7 +162,7 @@ class LocalTrainingService implements TrainingService { ...@@ -162,7 +162,7 @@ class LocalTrainingService implements TrainingService {
if (parseInt(code, 10) === 0) { if (parseInt(code, 10) === 0) {
this.setTrialJobStatus(trialJob, 'SUCCEEDED'); this.setTrialJobStatus(trialJob, 'SUCCEEDED');
} }
trialJob.endTime = new Date(parseInt(timestamp, 10)); trialJob.endTime = parseInt(timestamp, 10);
} }
} catch (error) { } catch (error) {
//ignore //ignore
...@@ -191,7 +191,7 @@ class LocalTrainingService implements TrainingService { ...@@ -191,7 +191,7 @@ class LocalTrainingService implements TrainingService {
const trialJobDetail: LocalTrialJobDetail = new LocalTrialJobDetail( const trialJobDetail: LocalTrialJobDetail = new LocalTrialJobDetail(
trialJobId, trialJobId,
'WAITING', 'WAITING',
new Date(), Date.now(),
path.join(this.rootDir, 'trials', trialJobId), path.join(this.rootDir, 'trials', trialJobId),
form); form);
this.jobQueue.push(trialJobId); this.jobQueue.push(trialJobId);
...@@ -339,7 +339,7 @@ class LocalTrainingService implements TrainingService { ...@@ -339,7 +339,7 @@ class LocalTrainingService implements TrainingService {
const process: cp.ChildProcess = cp.exec(`bash ${path.join(trialJobDetail.workingDirectory, 'run.sh')}`); const process: cp.ChildProcess = cp.exec(`bash ${path.join(trialJobDetail.workingDirectory, 'run.sh')}`);
this.setTrialJobStatus(trialJobDetail, 'RUNNING'); this.setTrialJobStatus(trialJobDetail, 'RUNNING');
trialJobDetail.startTime = new Date(); trialJobDetail.startTime = Date.now();
trialJobDetail.pid = process.pid; trialJobDetail.pid = process.pid;
this.setExtraProperties(trialJobDetail, resource); this.setExtraProperties(trialJobDetail, resource);
...@@ -372,7 +372,7 @@ class LocalTrainingService implements TrainingService { ...@@ -372,7 +372,7 @@ class LocalTrainingService implements TrainingService {
const jobDetail: LocalTrialJobDetail = { const jobDetail: LocalTrialJobDetail = {
id: jobId, id: jobId,
status: 'RUNNING', status: 'RUNNING',
submitTime: new Date(), submitTime: Date.now(),
workingDirectory: workDir, workingDirectory: workDir,
form: form, form: form,
pid: process.pid pid: process.pid
......
/**
* Copyright (c) Microsoft Corporation
* All rights reserved.
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
* to permit persons to whom the Software is furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
\ No newline at end of file
...@@ -19,11 +19,12 @@ ...@@ -19,11 +19,12 @@
'use strict'; 'use strict';
import * as assert from 'assert';
import { Client } from 'ssh2'; import { Client } from 'ssh2';
import { Deferred } from 'ts-deferred';
import { getLogger, Logger } from '../../common/log'; import { getLogger, Logger } from '../../common/log';
import { randomSelect } from '../../common/utils';
import { GPUInfo } from '../common/gpuData'; import { GPUInfo } from '../common/gpuData';
import { RemoteMachineMeta, RemoteMachineScheduleResult, RemoteMachineScheduleInfo, ScheduleResultType } from './remoteMachineData'; import { RemoteMachineMeta, RemoteMachineScheduleResult, ScheduleResultType } from './remoteMachineData';
/** /**
* A simple GPU scheduler implementation * A simple GPU scheduler implementation
...@@ -45,82 +46,64 @@ export class GPUScheduler { ...@@ -45,82 +46,64 @@ export class GPUScheduler {
* Schedule a machine according to the constraints (requiredGPUNum) * Schedule a machine according to the constraints (requiredGPUNum)
* @param requiredGPUNum required GPU number * @param requiredGPUNum required GPU number
*/ */
public scheduleMachine(requiredGPUNum : Number | undefined, trialJobId : string) : RemoteMachineScheduleResult { public scheduleMachine(requiredGPUNum: number, trialJobId : string) : RemoteMachineScheduleResult {
const deferred: Deferred<RemoteMachineScheduleResult> = new Deferred<RemoteMachineScheduleResult>(); assert(requiredGPUNum >= 0);
let scheduleResult : RemoteMachineScheduleResult = { const allRMs: RemoteMachineMeta[] = Array.from(this.machineSSHClientMap.keys());
resultType : ScheduleResultType.TMP_NO_AVAILABLE_GPU, assert(allRMs.length > 0);
scheduleInfo : undefined
}; // Step 1: Check if required GPU number not exceeds the total GPU number in all machines
const eligibleRM: RemoteMachineMeta[] = allRMs.filter((rmMeta : RemoteMachineMeta) =>
// Step 0: Check if required GPU number not exceeds the total GPU number in all machines rmMeta.gpuSummary === undefined || requiredGPUNum === 0 || rmMeta.gpuSummary.gpuCount >= requiredGPUNum);
const eligibleRM : RemoteMachineMeta[] = Array.from(this.machineSSHClientMap.keys()).filter((rmMeta : RemoteMachineMeta) => if (eligibleRM.length === 0) {
rmMeta.gpuSummary === undefined || requiredGPUNum === undefined || rmMeta.gpuSummary.gpuCount >= requiredGPUNum );
if(eligibleRM.length == 0) {
// If the required gpu number exceeds the upper limit of all machine's GPU number // If the required gpu number exceeds the upper limit of all machine's GPU number
// Return REQUIRE_EXCEED_TOTAL directly // Return REQUIRE_EXCEED_TOTAL directly
return ({ return ({
resultType : ScheduleResultType.REQUIRE_EXCEED_TOTAL, resultType: ScheduleResultType.REQUIRE_EXCEED_TOTAL,
scheduleInfo : undefined scheduleInfo: undefined
}); });
} }
// Step 1: Generate GPU resource map for remote machines // Step 2: Allocate Host/GPU for specified trial job
const totalResourceMap : Map<RemoteMachineMeta, GPUInfo[]> = this.gpuResourceDetection(requiredGPUNum); // Currenty the requireGPUNum parameter for all trial jobs are identical.
if (requiredGPUNum > 0) {
// Step 2: Find machine whose GPU can be allocated based on user GPU requirement, and allocate GPU // Trial job requires GPU
for (const rmMeta of Array.from(totalResourceMap.keys())) { const result: RemoteMachineScheduleResult | undefined = this.scheduleGPUHost(requiredGPUNum, trialJobId);
const gpuInfos : GPUInfo[] | undefined = totalResourceMap.get(rmMeta); if (result !== undefined) {
if(gpuInfos !== undefined && (requiredGPUNum === undefined || gpuInfos.length >= requiredGPUNum)) { return result;
const allocatedGPUIndex : number[] = Array();
// Allocate
gpuInfos.forEach((gpuInfo : GPUInfo) => {
rmMeta.gpuReservation.set(gpuInfo.index, trialJobId);
allocatedGPUIndex.push(gpuInfo.index);
});
// Construct scheduling return object
const sshClient : Client | undefined = this.machineSSHClientMap.get(rmMeta);
if(sshClient !== undefined) {
this.log.info(`Found available machine, trialJobId is ${trialJobId}, ip is ${rmMeta.ip}, gpu allocated is ${allocatedGPUIndex.toString()}`);
// We found the first available machine whose GPU resource can match user requirement
return {
resultType : ScheduleResultType.SUCCEED,
scheduleInfo : {
rmMeta : rmMeta,
client : sshClient,
cuda_visible_device : allocatedGPUIndex.join(',')
}
};
}
}
}
// Step 3: If not found machine whose GPU is availabe, then find the first machine whose GPU summary is unknown
for (const rmMeta of Array.from(this.machineSSHClientMap.keys())) {
const client : Client | undefined = this.machineSSHClientMap.get(rmMeta);
if(rmMeta.gpuSummary == undefined && client !== undefined) {
// We found the firstmachine whose GPU summary is unknown
return {
resultType : ScheduleResultType.SUCCEED,
scheduleInfo :{
rmMeta : rmMeta,
client : client,
//Since gpu information is unknown, make all GPU resources visible to the job
cuda_visible_device : ''
}
};
} }
}; } else {
// Trail job does not need GPU
this.log.warning(`Scheduler: trialJob id ${trialJobId}, no machine can be scheduled, resolve as TMP_NO_AVAILABLE_GPU `); const allocatedRm: RemoteMachineMeta = this.selectMachine(allRMs);
// Otherwise, no machine can be scheduled, resolve as TMP_NO_AVAILABLE_GPU
return this.allocateHost(requiredGPUNum, allocatedRm, [], trialJobId);
}
this.log.warning(`Scheduler: trialJob id ${trialJobId}, no machine can be scheduled, return TMP_NO_AVAILABLE_GPU `);
return { return {
resultType : ScheduleResultType.TMP_NO_AVAILABLE_GPU, resultType : ScheduleResultType.TMP_NO_AVAILABLE_GPU,
scheduleInfo : undefined scheduleInfo : undefined
}; };
} }
private scheduleGPUHost(requiredGPUNum: number, trialJobId: string): RemoteMachineScheduleResult | undefined {
const totalResourceMap: Map<RemoteMachineMeta, GPUInfo[]> = this.gpuResourceDetection();
const qualifiedRMs: RemoteMachineMeta[] = [];
totalResourceMap.forEach((gpuInfos: GPUInfo[], rmMeta: RemoteMachineMeta) => {
if (gpuInfos !== undefined && gpuInfos.length >= requiredGPUNum) {
qualifiedRMs.push(rmMeta);
}
});
if (qualifiedRMs.length > 0) {
const allocatedRm: RemoteMachineMeta = this.selectMachine(qualifiedRMs);
const gpuInfos: GPUInfo[] | undefined = totalResourceMap.get(allocatedRm);
if (gpuInfos !== undefined) { // should always true
return this.allocateHost(requiredGPUNum, allocatedRm, gpuInfos, trialJobId);
} else {
assert(false, 'gpuInfos is undefined');
}
}
}
/** /**
* Detect available GPU resource for a remote machine * Detect available GPU resource for a remote machine
* @param rmMeta Remote machine metadata * @param rmMeta Remote machine metadata
...@@ -128,33 +111,56 @@ export class GPUScheduler { ...@@ -128,33 +111,56 @@ export class GPUScheduler {
* @param availableGPUMap available GPU resource filled by this detection * @param availableGPUMap available GPU resource filled by this detection
* @returns Available GPU number on this remote machine * @returns Available GPU number on this remote machine
*/ */
private gpuResourceDetection(requiredGPUNum : Number | undefined) : Map<RemoteMachineMeta, GPUInfo[]> { private gpuResourceDetection() : Map<RemoteMachineMeta, GPUInfo[]> {
const totalResourceMap : Map<RemoteMachineMeta, GPUInfo[]> = new Map<RemoteMachineMeta, GPUInfo[]>(); const totalResourceMap : Map<RemoteMachineMeta, GPUInfo[]> = new Map<RemoteMachineMeta, GPUInfo[]>();
this.machineSSHClientMap.forEach((client: Client, rmMeta: RemoteMachineMeta) => { this.machineSSHClientMap.forEach((client: Client, rmMeta: RemoteMachineMeta) => {
// Assgin totoal GPU count as init available GPU number // Assgin totoal GPU count as init available GPU number
if(rmMeta.gpuSummary !== undefined) { if (rmMeta.gpuSummary !== undefined) {
const availableGPUs : GPUInfo[] = Array(); const availableGPUs: GPUInfo[] = [];
if(rmMeta.gpuReservation === undefined) { if (rmMeta.gpuReservation === undefined) {
rmMeta.gpuReservation = new Map<number, string>(); rmMeta.gpuReservation = new Map<number, string>();
} }
const gpuReservation = rmMeta.gpuReservation;
rmMeta.gpuSummary.gpuInfos.forEach((gpuInfo: GPUInfo) => { rmMeta.gpuSummary.gpuInfos.forEach((gpuInfo: GPUInfo) => {
//this.log.info(`GPU index:${gpuInfo.index}, activeProcessNum is ${gpuInfo.activeProcessNum}, GPU reservation is ${JSON.stringify([...gpuReservation])}`); // if the GPU has active process, OR be reserved by a job,
// if the GPU has active process, OR be reserved by a job,
// We should NOT allocate this GPU // We should NOT allocate this GPU
if (gpuInfo.activeProcessNum === 0 if (gpuInfo.activeProcessNum === 0 && !rmMeta.gpuReservation.has(gpuInfo.index)) {
&& !gpuReservation.has(gpuInfo.index)
&& requiredGPUNum !== undefined
&& availableGPUs.length < requiredGPUNum) {
availableGPUs.push(gpuInfo); availableGPUs.push(gpuInfo);
} }
}); });
totalResourceMap.set(rmMeta, availableGPUs); totalResourceMap.set(rmMeta, availableGPUs);
} }
}); });
return totalResourceMap; return totalResourceMap;
} }
private selectMachine(rmMetas: RemoteMachineMeta[]): RemoteMachineMeta {
assert(rmMetas !== undefined && rmMetas.length > 0);
return randomSelect(rmMetas);
}
private selectGPUsForTrial(gpuInfos: GPUInfo[], requiredGPUNum: number): GPUInfo[] {
// Sequentially allocate GPUs
return gpuInfos.slice(0, requiredGPUNum);
}
private allocateHost(requiredGPUNum: number, rmMeta: RemoteMachineMeta,
gpuInfos: GPUInfo[], trialJobId: string): RemoteMachineScheduleResult {
assert(gpuInfos.length >= requiredGPUNum);
const allocatedGPUs: GPUInfo[] = this.selectGPUsForTrial(gpuInfos, requiredGPUNum);
allocatedGPUs.forEach((gpuInfo: GPUInfo) => {
rmMeta.gpuReservation.set(gpuInfo.index, trialJobId);
});
return {
resultType: ScheduleResultType.SUCCEED,
scheduleInfo: {
rmMeta: rmMeta,
cuda_visible_device: allocatedGPUs.map((gpuInfo: GPUInfo) => { return gpuInfo.index; }).join(',')
}
};
}
} }
...@@ -87,16 +87,16 @@ export class JobMetrics { ...@@ -87,16 +87,16 @@ export class JobMetrics {
export class RemoteMachineTrialJobDetail implements TrialJobDetail { export class RemoteMachineTrialJobDetail implements TrialJobDetail {
public id: string; public id: string;
public status: TrialJobStatus; public status: TrialJobStatus;
public submitTime: Date; public submitTime: number;
public startTime?: Date; public startTime?: number;
public endTime?: Date; public endTime?: number;
public tags?: string[]; public tags?: string[];
public url?: string; public url?: string;
public workingDirectory: string; public workingDirectory: string;
public form: JobApplicationForm; public form: JobApplicationForm;
public rmMeta?: RemoteMachineMeta; public rmMeta?: RemoteMachineMeta;
constructor(id: string, status: TrialJobStatus, submitTime: Date, workingDirectory: string, form: JobApplicationForm) { constructor(id: string, status: TrialJobStatus, submitTime: number, workingDirectory: string, form: JobApplicationForm) {
this.id = id; this.id = id;
this.status = status; this.status = status;
this.submitTime = submitTime; this.submitTime = submitTime;
...@@ -106,9 +106,9 @@ export class RemoteMachineTrialJobDetail implements TrialJobDetail { ...@@ -106,9 +106,9 @@ export class RemoteMachineTrialJobDetail implements TrialJobDetail {
} }
} }
export type RemoteMachineScheduleResult = { scheduleInfo : RemoteMachineScheduleInfo | undefined, resultType : ScheduleResultType}; export type RemoteMachineScheduleResult = { scheduleInfo : RemoteMachineScheduleInfo | undefined; resultType : ScheduleResultType};
export type RemoteMachineScheduleInfo = { client: Client; rmMeta : RemoteMachineMeta; cuda_visible_device : string}; export type RemoteMachineScheduleInfo = { rmMeta : RemoteMachineMeta; cuda_visible_device : string};
export enum ScheduleResultType { export enum ScheduleResultType {
/* Schedule succeeded*/ /* Schedule succeeded*/
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
'use strict'; 'use strict';
import * as assert from 'assert';
import * as cpp from 'child-process-promise'; import * as cpp from 'child-process-promise';
import { EventEmitter } from 'events'; import { EventEmitter } from 'events';
import * as fs from 'fs'; import * as fs from 'fs';
...@@ -54,8 +55,6 @@ import { SSHClientUtility } from './sshClientUtility'; ...@@ -54,8 +55,6 @@ import { SSHClientUtility } from './sshClientUtility';
class RemoteMachineTrainingService implements TrainingService { class RemoteMachineTrainingService implements TrainingService {
private machineSSHClientMap: Map<RemoteMachineMeta, Client>; private machineSSHClientMap: Map<RemoteMachineMeta, Client>;
private trialJobsMap: Map<string, RemoteMachineTrialJobDetail>; private trialJobsMap: Map<string, RemoteMachineTrialJobDetail>;
private experimentId: string | undefined;
// Experiment root directory
private expRootDir: string; private expRootDir: string;
private remoteExpRootDir: string; private remoteExpRootDir: string;
private trialConfig: TrialConfig | undefined; private trialConfig: TrialConfig | undefined;
...@@ -72,9 +71,8 @@ class RemoteMachineTrainingService implements TrainingService { ...@@ -72,9 +71,8 @@ class RemoteMachineTrainingService implements TrainingService {
this.machineSSHClientMap = new Map<RemoteMachineMeta, Client>(); this.machineSSHClientMap = new Map<RemoteMachineMeta, Client>();
this.gpuScheduler = new GPUScheduler(this.machineSSHClientMap); this.gpuScheduler = new GPUScheduler(this.machineSSHClientMap);
this.jobQueue = []; this.jobQueue = [];
this.experimentId = getExperimentId();
this.expRootDir = getExperimentRootDir(); this.expRootDir = getExperimentRootDir();
this.remoteExpRootDir = this.getRemoteModeExperimentRootDir(); this.remoteExpRootDir = this.getRemoteExperimentRootDir();
this.timer = timer; this.timer = timer;
this.log = getLogger(); this.log = getLogger();
} }
...@@ -183,7 +181,7 @@ class RemoteMachineTrainingService implements TrainingService { ...@@ -183,7 +181,7 @@ class RemoteMachineTrainingService implements TrainingService {
const trialJobDetail: RemoteMachineTrialJobDetail = new RemoteMachineTrialJobDetail( const trialJobDetail: RemoteMachineTrialJobDetail = new RemoteMachineTrialJobDetail(
trialJobId, trialJobId,
'WAITING', 'WAITING',
new Date(), Date.now(),
trialWorkingFolder, trialWorkingFolder,
form); form);
this.jobQueue.push(trialJobId); this.jobQueue.push(trialJobId);
...@@ -326,6 +324,7 @@ class RemoteMachineTrainingService implements TrainingService { ...@@ -326,6 +324,7 @@ class RemoteMachineTrainingService implements TrainingService {
} }
this.machineSSHClientMap.set(rmMeta, conn); this.machineSSHClientMap.set(rmMeta, conn);
conn.on('ready', async () => { conn.on('ready', async () => {
this.machineSSHClientMap.set(rmMeta, conn);
await this.initRemoteMachineOnConnected(rmMeta, conn); await this.initRemoteMachineOnConnected(rmMeta, conn);
if (++connectedRMNum === rmMetaList.length) { if (++connectedRMNum === rmMetaList.length) {
deferred.resolve(); deferred.resolve();
...@@ -392,7 +391,7 @@ class RemoteMachineTrainingService implements TrainingService { ...@@ -392,7 +391,7 @@ class RemoteMachineTrainingService implements TrainingService {
trialJobDetail.status = 'RUNNING'; trialJobDetail.status = 'RUNNING';
trialJobDetail.url = `file://${rmScheduleInfo.rmMeta.ip}:${trialWorkingFolder}`; trialJobDetail.url = `file://${rmScheduleInfo.rmMeta.ip}:${trialWorkingFolder}`;
trialJobDetail.startTime = new Date(); trialJobDetail.startTime = Date.now();
trialJobDetail.rmMeta = rmScheduleInfo.rmMeta; trialJobDetail.rmMeta = rmScheduleInfo.rmMeta;
deferred.resolve(true); deferred.resolve(true);
...@@ -412,7 +411,13 @@ class RemoteMachineTrainingService implements TrainingService { ...@@ -412,7 +411,13 @@ class RemoteMachineTrainingService implements TrainingService {
throw new Error('trial config is not initialized'); throw new Error('trial config is not initialized');
} }
const cuda_visible_device: string = rmScheduleInfo.cuda_visible_device; const cuda_visible_device: string = rmScheduleInfo.cuda_visible_device;
const sshClient: Client = rmScheduleInfo.client; const sshClient: Client | undefined = this.machineSSHClientMap.get(rmScheduleInfo.rmMeta);
if (sshClient === undefined) {
assert(false, 'sshClient is undefined.');
// for lint
return;
}
const trialLocalTempFolder: string = path.join(this.expRootDir, 'trials-local', trialJobId); const trialLocalTempFolder: string = path.join(this.expRootDir, 'trials-local', trialJobId);
await SSHClientUtility.remoteExeCommand(`mkdir -p ${trialWorkingFolder}`, sshClient); await SSHClientUtility.remoteExeCommand(`mkdir -p ${trialWorkingFolder}`, sshClient);
...@@ -472,9 +477,9 @@ class RemoteMachineTrainingService implements TrainingService { ...@@ -472,9 +477,9 @@ class RemoteMachineTrainingService implements TrainingService {
path.join(localDir, 'run.sh'), path.join(remoteDir, 'run.sh'), sshClient); path.join(localDir, 'run.sh'), path.join(remoteDir, 'run.sh'), sshClient);
SSHClientUtility.remoteExeCommand(`bash ${path.join(remoteDir, 'run.sh')}`, sshClient); SSHClientUtility.remoteExeCommand(`bash ${path.join(remoteDir, 'run.sh')}`, sshClient);
const jobDetail: RemoteMachineTrialJobDetail = new RemoteMachineTrialJobDetail(jobId, 'RUNNING', new Date(), remoteDir, form); const jobDetail: RemoteMachineTrialJobDetail = new RemoteMachineTrialJobDetail(jobId, 'RUNNING', Date.now(), remoteDir, form);
jobDetail.rmMeta = rmMeta; jobDetail.rmMeta = rmMeta;
jobDetail.startTime = new Date(); jobDetail.startTime = Date.now();
this.trialJobsMap.set(jobId, jobDetail); this.trialJobsMap.set(jobId, jobDetail);
this.log.debug(`runHostJob: return: ${JSON.stringify(jobDetail)} `); this.log.debug(`runHostJob: return: ${JSON.stringify(jobDetail)} `);
...@@ -510,7 +515,7 @@ class RemoteMachineTrainingService implements TrainingService { ...@@ -510,7 +515,7 @@ class RemoteMachineTrainingService implements TrainingService {
} else { } else {
trialJob.status = 'FAILED'; trialJob.status = 'FAILED';
} }
trialJob.endTime = new Date(parseInt(timestamp, 10)); trialJob.endTime = parseInt(timestamp, 10);
} }
this.log.info(`trailJob status update: ${trialJob.id}, ${trialJob.status}`); this.log.info(`trailJob status update: ${trialJob.id}, ${trialJob.status}`);
} }
...@@ -536,7 +541,7 @@ class RemoteMachineTrainingService implements TrainingService { ...@@ -536,7 +541,7 @@ class RemoteMachineTrainingService implements TrainingService {
return path.join(this.remoteExpRootDir, 'hostjobs', jobId); return path.join(this.remoteExpRootDir, 'hostjobs', jobId);
} }
private getRemoteModeExperimentRootDir(): string{ private getRemoteExperimentRootDir(): string{
return path.join(os.tmpdir(), 'nni', 'experiments', getExperimentId()); return path.join(os.tmpdir(), 'nni', 'experiments', getExperimentId());
} }
......
...@@ -11,5 +11,12 @@ ...@@ -11,5 +11,12 @@
"no-console": [true, "log"], "no-console": [true, "log"],
"no-multiline-string": false "no-multiline-string": false
}, },
"rulesDirectory": [] "rulesDirectory": [],
"linterOptions": {
"exclude": [
"training_service/test/*",
"rest_server/test/*",
"core/test/*"
]
}
} }
\ No newline at end of file
...@@ -343,6 +343,24 @@ body-parser@1.18.2: ...@@ -343,6 +343,24 @@ body-parser@1.18.2:
raw-body "2.3.2" raw-body "2.3.2"
type-is "~1.6.15" type-is "~1.6.15"
boom@2.6.x:
version "2.6.1"
resolved "https://registry.yarnpkg.com/boom/-/boom-2.6.1.tgz#4dc8ef9b6dfad9c43bbbfbe71fa4c21419f22753"
dependencies:
hoek "2.x.x"
boxen@1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/boxen/-/boxen-1.3.0.tgz#55c6c39a8ba58d9c61ad22cd877532deb665a20b"
dependencies:
ansi-align "^2.0.0"
camelcase "^4.0.0"
chalk "^2.0.1"
cli-boxes "^1.0.0"
string-width "^2.0.0"
term-size "^1.2.0"
widest-line "^2.0.0"
brace-expansion@^1.1.7: brace-expansion@^1.1.7:
version "1.1.11" version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
...@@ -582,6 +600,38 @@ etag@~1.8.1: ...@@ -582,6 +600,38 @@ etag@~1.8.1:
version "1.8.1" version "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"
execa@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777"
dependencies:
cross-spawn "^5.0.1"
get-stream "^3.0.0"
is-stream "^1.1.0"
npm-run-path "^2.0.0"
p-finally "^1.0.0"
signal-exit "^3.0.0"
strip-eof "^1.0.0"
execa@^0.8.0:
version "0.8.0"
resolved "https://registry.yarnpkg.com/execa/-/execa-0.8.0.tgz#d8d76bbc1b55217ed190fd6dd49d3c774ecfc8da"
dependencies:
cross-spawn "^5.0.1"
get-stream "^3.0.0"
is-stream "^1.1.0"
npm-run-path "^2.0.0"
p-finally "^1.0.0"
signal-exit "^3.0.0"
strip-eof "^1.0.0"
express-joi-validator@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/express-joi-validator/-/express-joi-validator-2.0.0.tgz#24e26e6a8327f69985ed72588f00e295dc3e3234"
dependencies:
boom "2.6.x"
extend "2.0.x"
joi "6.x.x"
express@^4.16.3: express@^4.16.3:
version "4.16.3" version "4.16.3"
resolved "https://registry.yarnpkg.com/express/-/express-4.16.3.tgz#6af8a502350db3246ecc4becf6b5a34d22f7ed53" resolved "https://registry.yarnpkg.com/express/-/express-4.16.3.tgz#6af8a502350db3246ecc4becf6b5a34d22f7ed53"
...@@ -617,6 +667,10 @@ express@^4.16.3: ...@@ -617,6 +667,10 @@ express@^4.16.3:
utils-merge "1.0.1" utils-merge "1.0.1"
vary "~1.1.2" vary "~1.1.2"
extend@2.0.x:
version "2.0.2"
resolved "https://registry.yarnpkg.com/extend/-/extend-2.0.2.tgz#1b74985400171b85554894459c978de6ef453ab7"
extend@~3.0.1: extend@~3.0.1:
version "3.0.2" version "3.0.2"
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
...@@ -763,6 +817,10 @@ he@1.1.1: ...@@ -763,6 +817,10 @@ he@1.1.1:
version "1.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd"
hoek@2.x.x:
version "2.16.3"
resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed"
http-errors@1.6.2: http-errors@1.6.2:
version "1.6.2" version "1.6.2"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.2.tgz#0a002cc85707192a7e7946ceedc11155f60ec736" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.2.tgz#0a002cc85707192a7e7946ceedc11155f60ec736"
...@@ -852,6 +910,10 @@ isarray@~1.0.0: ...@@ -852,6 +910,10 @@ isarray@~1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
isemail@1.x.x:
version "1.2.0"
resolved "https://registry.yarnpkg.com/isemail/-/isemail-1.2.0.tgz#be03df8cc3e29de4d2c5df6501263f1fa4595e9a"
isexe@^2.0.0: isexe@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
...@@ -860,6 +922,15 @@ isstream@~0.1.2: ...@@ -860,6 +922,15 @@ isstream@~0.1.2:
version "0.1.2" version "0.1.2"
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
joi@6.x.x:
version "6.10.1"
resolved "https://registry.yarnpkg.com/joi/-/joi-6.10.1.tgz#4d50c318079122000fe5f16af1ff8e1917b77e06"
dependencies:
hoek "2.x.x"
isemail "1.x.x"
moment "2.x.x"
topo "1.x.x"
js-tokens@^3.0.2: js-tokens@^3.0.2:
version "3.0.2" version "3.0.2"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
...@@ -982,6 +1053,10 @@ mocha@^5.2.0: ...@@ -982,6 +1053,10 @@ mocha@^5.2.0:
mkdirp "0.5.1" mkdirp "0.5.1"
supports-color "5.4.0" supports-color "5.4.0"
moment@2.x.x:
version "2.22.2"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.22.2.tgz#3c257f9839fc0e93ff53149632239eb90783ff66"
ms@2.0.0: ms@2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
...@@ -1459,12 +1534,24 @@ tmp@^0.0.33: ...@@ -1459,12 +1534,24 @@ tmp@^0.0.33:
dependencies: dependencies:
os-tmpdir "~1.0.2" os-tmpdir "~1.0.2"
topo@1.x.x:
version "1.1.0"
resolved "https://registry.yarnpkg.com/topo/-/topo-1.1.0.tgz#e9d751615d1bb87dc865db182fa1ca0a5ef536d5"
dependencies:
hoek "2.x.x"
tough-cookie@~2.3.3: tough-cookie@~2.3.3:
version "2.3.4" version "2.3.4"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.4.tgz#ec60cee38ac675063ffc97a5c18970578ee83655" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.4.tgz#ec60cee38ac675063ffc97a5c18970578ee83655"
dependencies: dependencies:
punycode "^1.4.1" punycode "^1.4.1"
toxic@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/toxic/-/toxic-1.0.1.tgz#8c2e2528da591100adc3883f2c0e56acfb1c7288"
dependencies:
lodash "^4.17.10"
tree-kill@^1.2.0: tree-kill@^1.2.0:
version "1.2.0" version "1.2.0"
resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.0.tgz#5846786237b4239014f05db156b643212d4c6f36" resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.0.tgz#5846786237b4239014f05db156b643212d4c6f36"
......
...@@ -58,8 +58,6 @@ class Accuracy extends React.Component<{}, ChartState> { ...@@ -58,8 +58,6 @@ class Accuracy extends React.Component<{}, ChartState> {
yAxis: { yAxis: {
name: 'Accuracy', name: 'Accuracy',
type: 'value', type: 'value',
min: 0,
max: 1,
data: yAxis data: yAxis
}, },
series: [{ series: [{
...@@ -85,7 +83,7 @@ class Accuracy extends React.Component<{}, ChartState> { ...@@ -85,7 +83,7 @@ class Accuracy extends React.Component<{}, ChartState> {
accArr.push(parseFloat(accData[item].finalMetricData.data)); accArr.push(parseFloat(accData[item].finalMetricData.data));
} }
}); });
accY.push({yAxis: accArr}); accY.push({ yAxis: accArr });
let optionObj = this.getOption(accY[0]); let optionObj = this.getOption(accY[0]);
this.setState({ option: optionObj }, () => { this.setState({ option: optionObj }, () => {
if (accArr.length === 0) { if (accArr.length === 0) {
......
import * as React from 'react';
import axios from 'axios';
import { MANAGER_IP } from '../const';
import {
message,
Tabs,
Button
} from 'antd';
const TabPane = Tabs.TabPane;
import '../style/logdetail.css';
interface LogState {
trialId: string;
slotLog: string;
processLog: string;
}
class Logdetail extends React.Component<{}, LogState> {
public _isMounted = false;
constructor(props: {}) {
super(props);
this.state = {
trialId: '',
slotLog: '',
processLog: ''
};
}
getJobLog = () => {
Object.keys(this.props).map(item => {
if (item === 'location') {
if (this._isMounted) {
this.setState({ trialId: this.props[item].state }, () => {
const { trialId } = this.state;
let id = trialId;
axios(`${MANAGER_IP}/jobLog`, {
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=utf-8'
},
data: {
id
}
})
.then(res => {
if (res.status === 200 && this._isMounted) {
this.setState({
slotLog: res.data.trial_slot_log,
processLog: res.data.trial_process_log
});
}
});
});
}
}
});
}
getPaiDetail = (id: string) => {
axios(`${MANAGER_IP}/jobPaiPage`, {
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=utf-8'
},
data: {
id
}
})
.then(res => {
if (res.status === 200) {
message.success('Successful send');
setTimeout(this.openPage(res.data.url), 100);
}
});
}
openPage = (pailog: string) => {
window.open(pailog);
}
paiLog = () => {
axios(`${MANAGER_IP}/paiPage`, {
method: 'POST'
})
.then(res => {
if (res.status === 200) {
setTimeout(this.openPage(res.data.url), 200);
}
});
}
componentDidMount() {
this._isMounted = true;
this.getJobLog();
}
componentWillUnmount() {
this._isMounted = false;
}
render() {
const { trialId, slotLog, processLog } = this.state;
return (
<div className="log">
<div>
<Tabs type="card">
<TabPane tab="trial_slot_log" key="1">
<pre>{slotLog}</pre>
</TabPane>
<TabPane tab="trial_process_log" key="2">
<pre>{processLog}</pre>
</TabPane>
</Tabs>
</div>
<div className="pai">
<Button
type="primary"
className="tableButton marginTab"
onClick={this.getPaiDetail.bind(this, trialId)}
>
pai
</Button>
<Button
type="primary"
className="tableButton"
onClick={this.paiLog}
>
main job log
</Button>
</div>
</div>
);
}
}
export default Logdetail;
\ No newline at end of file
...@@ -35,6 +35,11 @@ interface ParaObj { ...@@ -35,6 +35,11 @@ interface ParaObj {
parallelAxis: Array<Dimobj>; parallelAxis: Array<Dimobj>;
} }
interface VisualMapValue {
maxAccuracy: number;
minAccuracy: number;
}
interface ParaState { interface ParaState {
option: object; option: object;
paraBack: ParaObj; paraBack: ParaObj;
...@@ -42,6 +47,7 @@ interface ParaState { ...@@ -42,6 +47,7 @@ interface ParaState {
swapAxisArr: Array<string>; swapAxisArr: Array<string>;
percent: number; percent: number;
paraNodata: string; paraNodata: string;
visualValue: VisualMapValue;
} }
message.config({ message.config({
...@@ -69,9 +75,13 @@ class Para extends React.Component<{}, ParaState> { ...@@ -69,9 +75,13 @@ class Para extends React.Component<{}, ParaState> {
swapAxisArr: [], swapAxisArr: [],
percent: 0, percent: 0,
paraNodata: '', paraNodata: '',
visualValue: {
minAccuracy: 0,
maxAccuracy: 1
}
}; };
} }
hyperParaPic = () => { hyperParaPic = () => {
axios axios
.all([ .all([
...@@ -110,7 +120,11 @@ class Para extends React.Component<{}, ParaState> { ...@@ -110,7 +120,11 @@ class Para extends React.Component<{}, ParaState> {
const dimName = Object.keys(speDimName[0]); const dimName = Object.keys(speDimName[0]);
if (this._isMounted) { if (this._isMounted) {
this.setState(() => ({ this.setState(() => ({
dimName: dimName dimName: dimName,
visualValue: {
minAccuracy: accPara.length !== 0 ? Math.min(...accPara) : 0,
maxAccuracy: accPara.length !== 0 ? Math.max(...accPara) : 1
}
})); }));
} }
// search space range and specific value [only number] // search space range and specific value [only number]
...@@ -159,6 +173,11 @@ class Para extends React.Component<{}, ParaState> { ...@@ -159,6 +173,11 @@ class Para extends React.Component<{}, ParaState> {
Object.keys(paraYdata).map(item => { Object.keys(paraYdata).map(item => {
paraYdata[item].push(accPara[item]); paraYdata[item].push(accPara[item]);
}); });
// according acc to sort ydata
if (paraYdata.length !== 0) {
const len = paraYdata[0].length - 1;
paraYdata.sort((a, b) => b[len] - a[len]);
}
this.setState(() => ({ this.setState(() => ({
paraBack: { paraBack: {
parallelAxis: parallelAxis, parallelAxis: parallelAxis,
...@@ -205,6 +224,7 @@ class Para extends React.Component<{}, ParaState> { ...@@ -205,6 +224,7 @@ class Para extends React.Component<{}, ParaState> {
// deal with response data into pic data // deal with response data into pic data
getOption = (dataObj: ParaObj) => { getOption = (dataObj: ParaObj) => {
const { visualValue } = this.state;
let parallelAxis = dataObj.parallelAxis; let parallelAxis = dataObj.parallelAxis;
let paralleData = dataObj.data; let paralleData = dataObj.data;
let optionown = { let optionown = {
...@@ -223,8 +243,6 @@ class Para extends React.Component<{}, ParaState> { ...@@ -223,8 +243,6 @@ class Para extends React.Component<{}, ParaState> {
borderColor: '#ddd' borderColor: '#ddd'
} }
}, },
feature: {
},
z: 202 z: 202
}, },
parallel: { parallel: {
...@@ -236,8 +254,8 @@ class Para extends React.Component<{}, ParaState> { ...@@ -236,8 +254,8 @@ class Para extends React.Component<{}, ParaState> {
}, },
visualMap: { visualMap: {
type: 'continuous', type: 'continuous',
min: 0, min: visualValue.minAccuracy,
max: 1, max: visualValue.maxAccuracy,
// gradient color // gradient color
color: ['#fb7c7c', 'yellow', 'lightblue'] color: ['#fb7c7c', 'yellow', 'lightblue']
}, },
...@@ -363,7 +381,7 @@ class Para extends React.Component<{}, ParaState> { ...@@ -363,7 +381,7 @@ class Para extends React.Component<{}, ParaState> {
<div className="paraTitle"> <div className="paraTitle">
<div className="paraLeft">Hyper Parameter</div> <div className="paraLeft">Hyper Parameter</div>
<div className="paraRight"> <div className="paraRight">
{/* <span>top</span> */} <span>top</span>
<Select <Select
className="parapercent" className="parapercent"
style={{ width: '20%' }} style={{ width: '20%' }}
......
import * as React from 'react'; import * as React from 'react';
import axios from 'axios'; import axios from 'axios';
import { Table, Select, Row, Col, Icon } from 'antd'; import { Table, Select, Row, Col, Icon } from 'antd';
import { MANAGER_IP, overviewItem, roundNum } from '../const'; import { MANAGER_IP, overviewItem } from '../const';
// import ReactEcharts from 'echarts-for-react';
const Option = Select.Option; const Option = Select.Option;
import JSONTree from 'react-json-tree'; import JSONTree from 'react-json-tree';
// require('echarts/lib/chart/line');
// require('echarts/lib/component/tooltip');
// require('echarts/lib/component/title');
require('../style/sessionpro.css'); require('../style/sessionpro.css');
interface TableObj { interface TableObj {
...@@ -183,6 +179,13 @@ class Sessionpro extends React.Component<{}, SessionState> { ...@@ -183,6 +179,13 @@ class Sessionpro extends React.Component<{}, SessionState> {
let sessionData = res.data; let sessionData = res.data;
let tunerAsstemp = []; let tunerAsstemp = [];
let trialPro = []; let trialPro = [];
const startExper = new Date(sessionData.startTime).toLocaleString();
let experEndStr: string;
if (sessionData.endTime !== undefined) {
experEndStr = new Date(sessionData.endTime).toLocaleString();
} else {
experEndStr = 'not over';
}
trialPro.push({ trialPro.push({
id: sessionData.id, id: sessionData.id,
author: sessionData.params.authorName, author: sessionData.params.authorName,
...@@ -191,8 +194,8 @@ class Sessionpro extends React.Component<{}, SessionState> { ...@@ -191,8 +194,8 @@ class Sessionpro extends React.Component<{}, SessionState> {
maxDuration: sessionData.params.maxExecDuration, maxDuration: sessionData.params.maxExecDuration,
execDuration: sessionData.execDuration, execDuration: sessionData.execDuration,
MaxTrialNum: sessionData.params.maxTrialNum, MaxTrialNum: sessionData.params.maxTrialNum,
startTime: sessionData.startTime, startTime: startExper,
endTime: sessionData.endTime === undefined ? 'not over' : sessionData.endTime endTime: experEndStr
}); });
tunerAsstemp.push({ tunerAsstemp.push({
tuner: sessionData.params.tuner, tuner: sessionData.params.tuner,
...@@ -225,14 +228,12 @@ class Sessionpro extends React.Component<{}, SessionState> { ...@@ -225,14 +228,12 @@ class Sessionpro extends React.Component<{}, SessionState> {
const desJobDetail: Parameters = { const desJobDetail: Parameters = {
parameters: {} parameters: {}
}; };
const startTime = Date.parse(tableData[item].startTime); const startTime = new Date(tableData[item].startTime).toLocaleString();
const duration = (Date.parse(tableData[item].endTime) - startTime) / 1000; const endTime = new Date(tableData[item].endTime).toLocaleString();
const duration = (tableData[item].endTime - tableData[item].startTime) / 1000;
let acc; let acc;
if (tableData[item].finalMetricData) { if (tableData[item].finalMetricData) {
const accFloat = parseFloat(tableData[item].finalMetricData.data); acc = parseFloat(tableData[item].finalMetricData.data);
acc = roundNum(accFloat, 5);
} else {
acc = 0;
} }
desJobDetail.parameters = JSON.parse(tableData[item].hyperParameters).parameters; desJobDetail.parameters = JSON.parse(tableData[item].hyperParameters).parameters;
if (tableData[item].logPath !== undefined) { if (tableData[item].logPath !== undefined) {
...@@ -242,8 +243,8 @@ class Sessionpro extends React.Component<{}, SessionState> { ...@@ -242,8 +243,8 @@ class Sessionpro extends React.Component<{}, SessionState> {
key: topTableData.length, key: topTableData.length,
id: tableData[item].id, id: tableData[item].id,
duration: duration, duration: duration,
start: tableData[item].startTime, start: startTime,
end: tableData[item].endTime, end: endTime,
status: tableData[item].status, status: tableData[item].status,
acc: acc, acc: acc,
description: desJobDetail description: desJobDetail
...@@ -265,27 +266,6 @@ class Sessionpro extends React.Component<{}, SessionState> { ...@@ -265,27 +266,6 @@ class Sessionpro extends React.Component<{}, SessionState> {
trialRun: trialRunData.sort(this.sortNumber) trialRun: trialRunData.sort(this.sortNumber)
}); });
} }
// draw CDF
// const { trialRun } = this.state;
// if (this._isMounted) {
// this.setState({
// option: this.getOption(trialRun)
// });
// }
// CDF graph 'No data' judge
// if (trialRun.length === 0) {
// if (this._isMounted) {
// this.setState({
// noData: 'No data'
// });
// }
// } else {
// if (this._isMounted) {
// this.setState({
// noData: ''
// });
// }
// }
} }
}); });
} }
...@@ -372,7 +352,7 @@ class Sessionpro extends React.Component<{}, SessionState> { ...@@ -372,7 +352,7 @@ class Sessionpro extends React.Component<{}, SessionState> {
}; };
const { const {
trialProfile, searchSpace, tunerAssessor, tableData, trialProfile, searchSpace, tunerAssessor, tableData,
// option, noData // option, noData
} = this.state; } = this.state;
let running; let running;
...@@ -424,9 +404,6 @@ class Sessionpro extends React.Component<{}, SessionState> { ...@@ -424,9 +404,6 @@ class Sessionpro extends React.Component<{}, SessionState> {
</p> </p>
<span>End Time</span> <span>End Time</span>
<p className="messcont">{trialProfile.endTime}</p> <p className="messcont">{trialProfile.endTime}</p>
{/* <div className="logo">
<Icon className="thrpink" type="clock-circle-o" />
</div> */}
</div> </div>
<div className="cdf"> <div className="cdf">
<div className="message"> <div className="message">
...@@ -501,13 +478,6 @@ class Sessionpro extends React.Component<{}, SessionState> { ...@@ -501,13 +478,6 @@ class Sessionpro extends React.Component<{}, SessionState> {
scroll={{ x: '100%', y: 540 }} scroll={{ x: '100%', y: 540 }}
/> />
</div> </div>
{/* <div className="cdf">
<ReactEcharts
option={option}
style={{ height: 500, padding: '0px' }}
/>
<div className="addNodata">{noData}</div>
</div> */}
</div> </div>
); );
} }
......
import * as React from 'react'; import * as React from 'react';
import { browserHistory } from 'react-router'; import { browserHistory } from 'react-router';
import axios from 'axios'; import axios from 'axios';
import { Table, Button, Popconfirm, message } from 'antd'; import { Table, Button, Popconfirm, message, Modal } from 'antd';
import { MANAGER_IP, roundNum, trialJobStatus } from '../const'; import { MANAGER_IP, trialJobStatus } from '../const';
import JSONTree from 'react-json-tree'; import JSONTree from 'react-json-tree';
import ReactEcharts from 'echarts-for-react'; import ReactEcharts from 'echarts-for-react';
const echarts = require('echarts/lib/echarts'); const echarts = require('echarts/lib/echarts');
require('echarts/lib/chart/bar'); require('echarts/lib/chart/bar');
require('echarts/lib/chart/line');
require('echarts/lib/chart/scatter');
require('echarts/lib/component/tooltip'); require('echarts/lib/component/tooltip');
require('echarts/lib/component/title'); require('echarts/lib/component/title');
require('../style/trialStatus.css'); require('../style/trialStatus.css');
...@@ -45,6 +47,8 @@ interface TabState { ...@@ -45,6 +47,8 @@ interface TabState {
downhref: string; downhref: string;
option: object; option: object;
trialJobs: object; trialJobs: object;
intermediateOption: object;
modalVisible: boolean;
} }
class TrialStatus extends React.Component<{}, TabState> { class TrialStatus extends React.Component<{}, TabState> {
...@@ -71,11 +75,50 @@ class TrialStatus extends React.Component<{}, TabState> { ...@@ -71,11 +75,50 @@ class TrialStatus extends React.Component<{}, TabState> {
}], }],
downhref: 'javascript:;', downhref: 'javascript:;',
option: {}, option: {},
trialJobs: {} intermediateOption: {},
// trialJobs: this.getTrialJobs() trialJobs: {},
modalVisible: false
}; };
} }
showIntermediateModal = (id: string) => {
axios(`${MANAGER_IP}/metric-data/${id}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json;charset=utf-8'
}
})
.then(res => {
if (res.status === 200) {
const intermediateArr: number[] = [];
const xinter: number[] = [];
Object.keys(res.data).map(item => {
intermediateArr.push(parseFloat(res.data[item].data));
xinter.push(res.data[item].sequence);
});
if (this._isMounted) {
this.setState({
intermediateOption: this.intermediateGraphOption(intermediateArr, id, xinter)
});
}
}
});
if (this._isMounted) {
this.setState({
modalVisible: true
});
}
}
hideIntermediateModal = () => {
if (this._isMounted) {
this.setState({
modalVisible: false
});
}
}
getOption = (dataObj: Runtrial) => { getOption = (dataObj: Runtrial) => {
let xAxis = dataObj.trialTime; let xAxis = dataObj.trialTime;
let yAxis = dataObj.trialId; let yAxis = dataObj.trialId;
...@@ -146,9 +189,9 @@ class TrialStatus extends React.Component<{}, TabState> { ...@@ -146,9 +189,9 @@ class TrialStatus extends React.Component<{}, TabState> {
const end = trialJobs[item].endTime; const end = trialJobs[item].endTime;
const start = trialJobs[item].startTime; const start = trialJobs[item].startTime;
if (start && end) { if (start && end) {
duration = (Date.parse(end) - Date.parse(start)) / 1000; duration = (end - start) / 1000;
} else { } else {
duration = (new Date().getTime() - Date.parse(start)) / 1000; duration = (new Date().getTime() - start) / 1000;
} }
trialId.push(trialJobs[item].id); trialId.push(trialJobs[item].id);
trialTime.push(duration); trialTime.push(duration);
...@@ -184,14 +227,11 @@ class TrialStatus extends React.Component<{}, TabState> { ...@@ -184,14 +227,11 @@ class TrialStatus extends React.Component<{}, TabState> {
const status = trialJobs[item].status !== undefined const status = trialJobs[item].status !== undefined
? trialJobs[item].status ? trialJobs[item].status
: ''; : '';
const acc = trialJobs[item].finalMetricData !== undefined
? roundNum(parseFloat(trialJobs[item].finalMetricData.data), 5)
: 0;
const startTime = trialJobs[item].startTime !== undefined const startTime = trialJobs[item].startTime !== undefined
? trialJobs[item].startTime ? new Date(trialJobs[item].startTime).toLocaleString()
: ''; : '';
const endTime = trialJobs[item].endTime !== undefined const endTime = trialJobs[item].endTime !== undefined
? trialJobs[item].endTime ? new Date(trialJobs[item].endTime).toLocaleString()
: ''; : '';
let desc: DescObj = { let desc: DescObj = {
parameters: {} parameters: {}
...@@ -202,11 +242,15 @@ class TrialStatus extends React.Component<{}, TabState> { ...@@ -202,11 +242,15 @@ class TrialStatus extends React.Component<{}, TabState> {
if (trialJobs[item].logPath !== undefined) { if (trialJobs[item].logPath !== undefined) {
desc.logPath = trialJobs[item].logPath; desc.logPath = trialJobs[item].logPath;
} }
let acc = 0;
if (trialJobs[item].finalMetricData !== undefined) {
acc = parseFloat(trialJobs[item].finalMetricData.data);
}
let duration = 0; let duration = 0;
if (startTime !== '' && endTime !== '') { if (startTime !== '' && endTime !== '') {
duration = (Date.parse(endTime) - Date.parse(startTime)) / 1000; duration = (trialJobs[item].endTime - trialJobs[item].startTime) / 1000;
} else if (startTime !== '' && endTime === '') { } else if (startTime !== '' && endTime === '') {
duration = (new Date().getTime() - Date.parse(startTime)) / 1000; duration = (new Date().getTime() - trialJobs[item].startTime) / 1000;
} else { } else {
duration = 0; duration = 0;
} }
...@@ -262,6 +306,28 @@ class TrialStatus extends React.Component<{}, TabState> { ...@@ -262,6 +306,28 @@ class TrialStatus extends React.Component<{}, TabState> {
browserHistory.push(path); browserHistory.push(path);
} }
intermediateGraphOption = (intermediateArr: number[], id: string, xinter: number[]) => {
return {
tooltip: {
trigger: 'item'
},
xAxis: {
name: 'Trial',
data: xinter
},
yAxis: {
name: 'Accuracy',
type: 'value',
data: intermediateArr
},
series: [{
symbolSize: 6,
type: 'scatter',
data: intermediateArr
}]
};
}
componentDidMount() { componentDidMount() {
this._isMounted = true; this._isMounted = true;
...@@ -281,6 +347,7 @@ class TrialStatus extends React.Component<{}, TabState> { ...@@ -281,6 +347,7 @@ class TrialStatus extends React.Component<{}, TabState> {
} }
render() { render() {
const { intermediateOption, modalVisible, option, tableData } = this.state;
let bgColor = ''; let bgColor = '';
const trialJob: Array<TrialJob> = []; const trialJob: Array<TrialJob> = [];
trialJobStatus.map(item => { trialJobStatus.map(item => {
...@@ -401,11 +468,17 @@ class TrialStatus extends React.Component<{}, TabState> { ...@@ -401,11 +468,17 @@ class TrialStatus extends React.Component<{}, TabState> {
getItemString={() => (<span />)} // remove the {} items getItemString={() => (<span />)} // remove the {} items
data={record.description} data={record.description}
/> />
<Button
type="primary"
className="tableButton"
onClick={this.showIntermediateModal.bind(this, record.id)}
>
Intermediate Result
</Button>
</pre> </pre>
); );
}; };
const { option, tableData } = this.state;
return ( return (
<div className="hyper" id="tableCenter"> <div className="hyper" id="tableCenter">
<ReactEcharts <ReactEcharts
...@@ -422,6 +495,23 @@ class TrialStatus extends React.Component<{}, TabState> { ...@@ -422,6 +495,23 @@ class TrialStatus extends React.Component<{}, TabState> {
bordered={true} bordered={true}
scroll={{ x: '100%', y: window.innerHeight * 0.78 }} scroll={{ x: '100%', y: window.innerHeight * 0.78 }}
/> />
<Modal
title="Intermediate Result"
visible={modalVisible}
onCancel={this.hideIntermediateModal}
footer={null}
destroyOnClose={true}
width="80%"
>
<ReactEcharts
option={intermediateOption}
style={{
width: '100%',
height: 0.7 * window.innerHeight
}}
theme="my_theme"
/>
</Modal>
</div> </div>
); );
} }
......
...@@ -14,4 +14,3 @@ export const CONTROLTYPE = [ ...@@ -14,4 +14,3 @@ export const CONTROLTYPE = [
'MAX_EXEC_DURATION' 'MAX_EXEC_DURATION'
]; ];
export const overviewItem = 5; export const overviewItem = 5;
export const roundNum = (acc: number, n: number) => Math.round(acc * 10 ** n) / 10 ** n;
...@@ -10,12 +10,10 @@ import TrialStatus from './components/TrialStatus'; ...@@ -10,12 +10,10 @@ import TrialStatus from './components/TrialStatus';
import Tensor from './components/Tensor'; import Tensor from './components/Tensor';
import Control from './components/Control'; import Control from './components/Control';
import Sessionpro from './components/Sessionpro'; import Sessionpro from './components/Sessionpro';
import Logdetail from './components/Logdetail';
import './index.css'; import './index.css';
ReactDOM.render( ReactDOM.render(
<Router history={browserHistory}> <Router history={browserHistory}>
<Route path="/log" component={Logdetail} />
<Route path="/" component={App}> <Route path="/" component={App}>
<IndexRedirect to="/oview" /> <IndexRedirect to="/oview" />
<Route path="/oview" component={Sessionpro} /> <Route path="/oview" component={Sessionpro} />
......
...@@ -52,8 +52,8 @@ pre.hyperpar{ ...@@ -52,8 +52,8 @@ pre.hyperpar{
Button.tableButton{ Button.tableButton{
background: #3c8dbc; background: #3c8dbc;
border-color: #3c8dbc; border-color: #3c8dbc;
height: 26px; height: 28px;
line-height: 26px; line-height: 28px;
/* padding: 3px 4px; */ /* padding: 3px 4px; */
} }
/* status bgcolor */ /* status bgcolor */
...@@ -98,6 +98,9 @@ Button.tableButton:hover, Button.tableButton:focus{ ...@@ -98,6 +98,9 @@ Button.tableButton:hover, Button.tableButton:focus{
background-color: #3c8dbc; background-color: #3c8dbc;
border-color: #3c8dbc; border-color: #3c8dbc;
} }
.ant-modal-title{
font-size: 20px;
}
...@@ -12,10 +12,16 @@ tuner: ...@@ -12,10 +12,16 @@ tuner:
codeDir: . codeDir: .
classFileName: naive_tuner.py classFileName: naive_tuner.py
className: NaiveTuner className: NaiveTuner
classArgs:
optimize_mode: maximize
gpuNum: 0
assessor: assessor:
codeDir: . codeDir: .
classFileName: naive_assessor.py classFileName: naive_assessor.py
className: NaiveAssessor className: NaiveAssessor
classArgs:
optimize_mode: maximize
gpuNum: 0
trial: trial:
command: python3 naive_trial.py command: python3 naive_trial.py
codeDir: . codeDir: .
......
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