experiment.ts 6.04 KB
Newer Older
1
import { MANAGER_IP } from '../const';
2
import { ExperimentConfig, toSeconds } from '../experimentConfig';
Yuge Zhang's avatar
Yuge Zhang committed
3
import { ExperimentProfile, ExperimentMetadata, NNIManagerStatus } from '../interface';
Lijiaoa's avatar
Lijiaoa committed
4
import { requestAxios } from '../function';
5
import { SearchSpace } from './searchspace';
6
7
8
9
10
11
12
13
14
15

function compareProfiles(profile1?: ExperimentProfile, profile2?: ExperimentProfile): boolean {
    if (!profile1 || !profile2) {
        return false;
    }
    const copy1 = Object.assign({}, profile1, { execDuration: undefined });
    const copy2 = Object.assign({}, profile2, { execDuration: undefined });
    return JSON.stringify(copy1) === JSON.stringify(copy2);
}

16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
const emptyProfile: ExperimentProfile = {
    params: {
        searchSpace: undefined,
        trialCommand: '',
        trialCodeDirectory: '',
        trialConcurrency: 0,
        debug: false,
        trainingService: {
            platform: ''
        }
    },
    id: '',
    execDuration: 0,
    logDir: '',
    startTime: 0,
    maxSequenceId: 0,
    revision: 0
};

Yuge Zhang's avatar
Yuge Zhang committed
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
const emptyMetadata: ExperimentMetadata = {
    id: '',
    port: 0,
    startTime: '',
    endTime: '',
    status: '',
    platform: '',
    experimentName: '',
    tag: [],
    pid: 0,
    webuiUrl: [],
    logDir: '',
    prefixUrl: null
};

50
class Experiment {
51
    private profileField?: ExperimentProfile;
Yuge Zhang's avatar
Yuge Zhang committed
52
    private metadataField?: ExperimentMetadata = undefined;
53
    private statusField?: NNIManagerStatus = undefined;
54
    private isNestedExperiment: boolean = false;
Lijiaoa's avatar
Lijiaoa committed
55
56
57
58
    private isexperimentError: boolean = false;
    private experimentErrorMessage: string = '';
    private isStatusError: boolean = false;
    private statusErrorMessage: string = '';
59
60
61

    public async init(): Promise<void> {
        while (!this.profileField || !this.statusField) {
Lijiaoa's avatar
Lijiaoa committed
62
63
64
65
66
67
            if (this.isexperimentError) {
                return;
            }
            if (this.isStatusError) {
                return;
            }
68
69
70
71
            await this.update();
        }
    }

72
    public isNestedExp(): boolean {
73
74
75
76
77
78
79
        try {
            return !!Object.values(this.config.searchSpace).find(
                item => (item as any)._value && typeof (item as any)._value[0] == 'object'
            );
        } catch {
            return false;
        }
80
81
    }

Lijiaoa's avatar
Lijiaoa committed
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
    public experimentError(): boolean {
        return this.isexperimentError;
    }

    public statusError(): boolean {
        return this.isStatusError;
    }

    public getExperimentMessage(): string {
        return this.experimentErrorMessage;
    }

    public getStatusMessage(): string {
        return this.statusErrorMessage;
    }

98
99
    public async update(): Promise<boolean> {
        let updated = false;
Lijiaoa's avatar
Lijiaoa committed
100

Yuge Zhang's avatar
Yuge Zhang committed
101
102
103
104
105
106
107
108
        await Promise.all([requestAxios(`${MANAGER_IP}/experiment`), requestAxios(`${MANAGER_IP}/experiment-metadata`)])
            .then(([profile, metadata]) => {
                updated ||= !compareProfiles(this.profileField, profile);
                this.profileField = profile;

                if (JSON.stringify(this.metadataField) !== JSON.stringify(metadata)) {
                    this.metadataField = metadata;
                }
Lijiaoa's avatar
Lijiaoa committed
109
110
111
112
113
114
115
116
117
            })
            .catch(error => {
                this.isexperimentError = true;
                this.experimentErrorMessage = `${error.message}`;
                updated = true;
            });

        await requestAxios(`${MANAGER_IP}/check-status`)
            .then(data => {
Lijiaoa's avatar
Lijiaoa committed
118
                updated = JSON.stringify(this.statusField) !== JSON.stringify(data);
Lijiaoa's avatar
Lijiaoa committed
119
120
121
122
123
124
125
126
                this.statusField = data;
            })
            .catch(error => {
                this.isStatusError = true;
                this.statusErrorMessage = `${error.message}`;
                updated = true;
            });

127
128
129
130
        return updated;
    }

    get profile(): ExperimentProfile {
Lijiaoa's avatar
Lijiaoa committed
131
        return this.profileField ?? emptyProfile;
132
133
    }

Yuge Zhang's avatar
Yuge Zhang committed
134
    get metadata(): ExperimentMetadata {
Lijiaoa's avatar
Lijiaoa committed
135
        return this.metadataField ?? emptyMetadata;
Yuge Zhang's avatar
Yuge Zhang committed
136
137
    }

138
139
140
141
142
    get config(): ExperimentConfig {
        return this.profile.params;
    }

    get maxExperimentDurationSeconds(): number {
liuzhe-lz's avatar
liuzhe-lz committed
143
        const value = this.config.maxExperimentDuration || (this.config as any).maxExecDuration;
144
145
146
147
        return value === undefined ? Infinity : toSeconds(value);
    }

    get maxTrialNumber(): number {
liuzhe-lz's avatar
liuzhe-lz committed
148
        const value = this.config.maxTrialNumber || (this.config as any).maxTrialNum;
Lijiaoa's avatar
Lijiaoa committed
149
        return value ?? Infinity;
150
151
152
    }

    get trialConcurrency(): number {
153
        return this.config.trialConcurrency;
154
155
156
    }

    get optimizeMode(): string {
157
        for (const algo of [this.config.tuner, this.config.advisor, this.config.assessor]) {
Lijiaoa's avatar
Lijiaoa committed
158
159
            if (algo && algo.classArgs && algo.classArgs['optimize_mode']) {
                return algo.classArgs['optimize_mode'];
160
161
162
            }
        }
        return 'unknown';
163
164
165
    }

    get trainingServicePlatform(): string {
liuzhe-lz's avatar
liuzhe-lz committed
166
167
168
169
170
171
172
        if (Array.isArray(this.config.trainingService)) {
            return 'hybrid';
        } else if (this.config.trainingService) {
            return this.config.trainingService.platform;
        } else {
            return (this.config as any).trainingServicePlatform;
        }
173
174
175
    }

    get searchSpace(): object {
176
        return this.config.searchSpace;
177
178
    }

179
180
181
182
183
184
    get searchSpaceNew(): SearchSpace {
        // The search space derived directly from profile
        // eventually this will replace searchSpace
        return new SearchSpace('', '', this.searchSpace);
    }

185
186
    get status(): string {
        if (!this.statusField) {
Lijiaoa's avatar
Lijiaoa committed
187
188
189
            // throw Error('Experiment status not initialized');
            // this.statusField.status = '';
            return '';
190
        }
Lijiao's avatar
Lijiao committed
191
        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
192
193
194
195
        return this.statusField!.status;
    }

    get error(): string {
196
        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
197
198
199
        if (!this.statusField) {
            throw Error('Experiment status not initialized');
        }
Lijiao's avatar
Lijiao committed
200
        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
201
202
203
204
205
        return this.statusField!.errors[0] || '';
    }
}

export { Experiment };