Skip to content

Commit 63b1178

Browse files
author
Anuar Talipov
committed
Add a color by regex feature that matches the experiment name.
1 parent a5f87e4 commit 63b1178

10 files changed

+178
-109
lines changed

tensorboard/webapp/runs/actions/runs_actions.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,14 @@ export const runColorChanged = createAction(
7777
props<{runId: string; newColor: string}>()
7878
);
7979

80-
export const runGroupByChanged =
81-
createAction('[Runs] Run Group By Changed', props<{
82-
experimentIds: string[]; groupBy: GroupBy;
83-
expNameByExpId?: Record<string, string>
84-
}>());
80+
export const runGroupByChanged = createAction(
81+
'[Runs] Run Group By Changed',
82+
props<{
83+
experimentIds: string[];
84+
groupBy: GroupBy;
85+
expNameByExpId?: Record<string, string>;
86+
}>()
87+
);
8588

8689
/**
8790
* Inserts the provided column header at the specified index.

tensorboard/webapp/runs/store/runs_reducers.ts

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,8 @@ const dataReducer: ActionReducer<RunsDataState, Action> = createReducer(
118118
let {colorGroupRegexString, userSetGroupByKey} = state;
119119
if (groupBy) {
120120
const regexString =
121-
groupBy.key === GroupByKey.REGEX || groupBy.key === GroupByKey.REGEX_BY_EXP
121+
groupBy.key === GroupByKey.REGEX ||
122+
groupBy.key === GroupByKey.REGEX_BY_EXP
122123
? groupBy.regexString
123124
: state.colorGroupRegexString;
124125
colorGroupRegexString = regexString;
@@ -244,7 +245,10 @@ const dataReducer: ActionReducer<RunsDataState, Action> = createReducer(
244245
}),
245246
on(
246247
runsActions.runGroupByChanged,
247-
(state: RunsDataState, {experimentIds, groupBy, expNameByExpId}): RunsDataState => {
248+
(
249+
state: RunsDataState,
250+
{experimentIds, groupBy, expNameByExpId}
251+
): RunsDataState => {
248252
// Reset the groupKeyToColorId
249253
const groupKeyToColorId = new Map<string, number>();
250254
const defaultRunColorIdForGroupBy = new Map(
@@ -255,8 +259,12 @@ const dataReducer: ActionReducer<RunsDataState, Action> = createReducer(
255259
.flatMap((experimentId) => state.runIds[experimentId])
256260
.map((runId) => state.runMetadata[runId]);
257261

258-
const groups =
259-
groupRuns(groupBy, allRuns, state.runIdToExpId, expNameByExpId);
262+
const groups = groupRuns(
263+
groupBy,
264+
allRuns,
265+
state.runIdToExpId,
266+
expNameByExpId
267+
);
260268

261269
Object.entries(groups.matches).forEach(([groupId, runs]) => {
262270
const colorId =
@@ -273,10 +281,11 @@ const dataReducer: ActionReducer<RunsDataState, Action> = createReducer(
273281
defaultRunColorIdForGroupBy.set(run.id, -1);
274282
}
275283

276-
const updatedRegexString = (groupBy.key === GroupByKey.REGEX ||
277-
groupBy.key === GroupByKey.REGEX_BY_EXP) ?
278-
groupBy.regexString :
279-
state.colorGroupRegexString;
284+
const updatedRegexString =
285+
groupBy.key === GroupByKey.REGEX ||
286+
groupBy.key === GroupByKey.REGEX_BY_EXP
287+
? groupBy.regexString
288+
: state.colorGroupRegexString;
280289

281290
return {
282291
...state,

tensorboard/webapp/runs/store/utils.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@ import {GroupBy, GroupByKey, Run, RunGroup} from '../types';
1717
import {ExperimentId, RunId} from './runs_types';
1818

1919
export function groupRuns(
20-
groupBy: GroupBy, runs: Run[],
21-
runIdToExpId: Readonly<Record<RunId, ExperimentId>>,
22-
expNameByExpId?: Record<string, string>): RunGroup {
20+
groupBy: GroupBy,
21+
runs: Run[],
22+
runIdToExpId: Readonly<Record<RunId, ExperimentId>>,
23+
expNameByExpId?: Record<string, string>
24+
): RunGroup {
2325
const matches: {[id: string]: Run[]} = {};
2426
const nonMatches: Run[] = [];
2527
const runGroup: RunGroup = {matches, nonMatches};
@@ -93,8 +95,9 @@ export function groupRuns(
9395
const hasCapturingGroup = matchesList.length > 1;
9496
// In case regex string does not have a capturing group, we use pseudo
9597
// group id of `pseudo_group`.
96-
const id = hasCapturingGroup ? JSON.stringify(matchesList.slice(1)) :
97-
'pseudo_group';
98+
const id = hasCapturingGroup
99+
? JSON.stringify(matchesList.slice(1))
100+
: 'pseudo_group';
98101
const runs = matches[id] || [];
99102
runs.push(run);
100103
matches[id] = runs;

tensorboard/webapp/runs/store/utils_test.ts

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -256,8 +256,8 @@ describe('run store utils test', () => {
256256
'eid2/gamma': 'eid2',
257257
},
258258
{
259-
'eid1': 'experiment1',
260-
'eid2': 'expiriment2'
259+
eid1: 'experiment1',
260+
eid2: 'expiriment2',
261261
}
262262
);
263263
expect(actual).toEqual({
@@ -282,8 +282,8 @@ describe('run store utils test', () => {
282282
'eid2/gamma': 'eid2',
283283
},
284284
{
285-
'eid1': 'experiment1',
286-
'eid2': 'expiriment2'
285+
eid1: 'experiment1',
286+
eid2: 'expiriment2',
287287
}
288288
);
289289
expect(actual).toEqual({
@@ -307,12 +307,12 @@ describe('run store utils test', () => {
307307
'eid1/beta': 'eid1',
308308
'eid2/beta': 'eid2',
309309
'eid2/gamma': 'eid2',
310-
'eid3/theta': 'eid3'
310+
'eid3/theta': 'eid3',
311311
},
312312
{
313-
'eid1': 'foobar',
314-
'eid2': 'bar',
315-
'eid3': 'foodoo'
313+
eid1: 'foobar',
314+
eid2: 'bar',
315+
eid3: 'foodoo',
316316
}
317317
);
318318

@@ -353,10 +353,10 @@ describe('run store utils test', () => {
353353
'eid4/gamma': 'eid4',
354354
},
355355
{
356-
'eid1': 'foo1bar',
357-
'eid2': 'foo2bar',
358-
'eid3': 'foo1bar',
359-
'eid4': 'theta',
356+
eid1: 'foo1bar',
357+
eid2: 'foo2bar',
358+
eid3: 'foo1bar',
359+
eid4: 'theta',
360360
}
361361
);
362362

@@ -439,7 +439,10 @@ describe('run store utils test', () => {
439439
it('groups by experiment name regex', () => {
440440
const actual = createGroupBy(GroupByKey.REGEX_BY_EXP, 'world');
441441

442-
expect(actual).toEqual({key: GroupByKey.REGEX_BY_EXP, regexString: 'world'});
442+
expect(actual).toEqual({
443+
key: GroupByKey.REGEX_BY_EXP,
444+
regexString: 'world',
445+
});
443446
});
444447
});
445448
});

tensorboard/webapp/runs/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export enum GroupByKey {
3737
// the grouping is to be defined.
3838
REGEX,
3939
// Group runs by regex that matches on the experiment name.
40-
REGEX_BY_EXP
40+
REGEX_BY_EXP,
4141
}
4242

4343
export interface BaseGroupBy {

tensorboard/webapp/runs/views/runs_table/regex_edit_dialog_component.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ import {
2323
} from '@angular/core';
2424
import {MatDialogRef} from '@angular/material/dialog';
2525
import {GroupByKey, Run} from '../../types';
26-
import { MatSelectChange } from '@angular/material/select';
27-
import { ChangeDetectorRef } from '@angular/core';
26+
import {MatSelectChange} from '@angular/material/select';
27+
import {ChangeDetectorRef} from '@angular/core';
2828

2929
export interface ColorGroup {
3030
groupId: string;
@@ -59,7 +59,10 @@ export class RegexEditDialogComponent {
5959
) {}
6060

6161
ngOnInit() {
62-
this.regexMatchType = this.selectedGroupBy === GroupByKey.REGEX_BY_EXP ? 'regex_by_exp' : 'regex_by_run';
62+
this.regexMatchType =
63+
this.selectedGroupBy === GroupByKey.REGEX_BY_EXP
64+
? 'regex_by_exp'
65+
: 'regex_by_run';
6366
}
6467

6568
private resetFocus() {
@@ -97,7 +100,9 @@ export class RegexEditDialogComponent {
97100
// This line is needed to update the value on the HTML element.
98101
this.cdRef.detectChanges();
99102
this.regexTypeOnChange.emit(
100-
event.value === 'regex_by_run' ? GroupByKey.REGEX :
101-
GroupByKey.REGEX_BY_EXP);
103+
event.value === 'regex_by_run'
104+
? GroupByKey.REGEX
105+
: GroupByKey.REGEX_BY_EXP
106+
);
102107
}
103108
}

tensorboard/webapp/runs/views/runs_table/regex_edit_dialog_container.ts

Lines changed: 61 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
filter,
2323
map,
2424
shareReplay,
25+
shareReplay,
2526
startWith,
2627
take,
2728
} from 'rxjs/operators';
@@ -33,6 +34,7 @@ import {
3334
getColorGroupRegexString,
3435
getRunIdsForExperiment,
3536
getRunGroupBy,
37+
getRunGroupBy,
3638
getRuns,
3739
} from '../../store/runs_selectors';
3840
import {groupRuns} from '../../store/utils';
@@ -48,8 +50,11 @@ const INPUT_CHANGE_DEBOUNCE_INTERVAL_MS = 500;
4850
[colorRunPairList]="colorRunPairList$ | async"
4951
[selectedGroupBy]="groupByRegexType$ | async"
5052
(onSave)="onSave()"
53+
[selectedGroupBy]="groupByRegexType$ | async"
54+
(onSave)="onSave()"
5155
(regexInputOnChange)="onRegexInputOnChange($event)"
5256
(regexTypeOnChange)="onRegexTypeOnChange($event)"
57+
(regexTypeOnChange)="onRegexTypeOnChange($event)"
5358
></regex-edit-dialog-component>`,
5459
styles: [
5560
`
@@ -65,12 +70,13 @@ const INPUT_CHANGE_DEBOUNCE_INTERVAL_MS = 500;
6570
export class RegexEditDialogContainer {
6671
private readonly experimentIds: string[];
6772
private readonly expNameByExpId: Record<string, string>;
73+
private readonly expNameByExpId: Record<string, string>;
6874
private readonly runIdToEid$: Observable<Record<string, string>>;
6975
private readonly allRuns$: Observable<Run[]>;
7076
private readonly tentativeRegexString$: Subject<string> =
71-
new Subject<string>();
77+
new Subject<string>();
7278
private readonly tentativeRegexType$: Subject<GroupByKey> =
73-
new Subject<GroupByKey>();
79+
new Subject<GroupByKey>();
7480

7581
readonly groupByRegexString$: Observable<string> = defer(() => {
7682
return merge(
@@ -79,17 +85,20 @@ export class RegexEditDialogContainer {
7985
);
8086
}).pipe(startWith(''), shareReplay(1));
8187

82-
readonly groupByRegexType$: Observable<GroupByKey> =
83-
merge(
84-
this.store.select(getRunGroupBy).pipe(take(1), map(group => group.key)),
85-
this.tentativeRegexType$
86-
).pipe(
87-
filter(
88-
key => key === GroupByKey.REGEX || key === GroupByKey.REGEX_BY_EXP),
89-
startWith(GroupByKey.REGEX),
90-
shareReplay(1)
91-
);
92-
88+
readonly groupByRegexType$: Observable<GroupByKey> = merge(
89+
this.store.select(getRunGroupBy).pipe(
90+
take(1),
91+
map((group) => group.key)
92+
),
93+
this.tentativeRegexType$
94+
).pipe(
95+
filter(
96+
(key) => key === GroupByKey.REGEX || key === GroupByKey.REGEX_BY_EXP
97+
),
98+
startWith(GroupByKey.REGEX),
99+
shareReplay(1)
100+
);
101+
93102
readonly colorRunPairList$: Observable<ColorGroup[]> = defer(() => {
94103
return this.groupByRegexString$.pipe(
95104
debounceTime(INPUT_CHANGE_DEBOUNCE_INTERVAL_MS),
@@ -103,20 +112,31 @@ export class RegexEditDialogContainer {
103112
}),
104113
combineLatestWith(
105114
this.groupByRegexType$,
106-
this.allRuns$, this.runIdToEid$,
115+
this.allRuns$,
116+
this.runIdToEid$,
107117
this.store.select(settingsSelectors.getColorPalette),
108118
this.store.select(getDarkModeEnabled)
109119
),
110-
map(([
111-
regexString, regexType, allRuns, runIdToEid, colorPalette,
112-
darkModeEanbled
113-
]) => {
120+
map(
121+
([
122+
regexString,
123+
regexType,
124+
allRuns,
125+
runIdToEid,
126+
colorPalette,
127+
darkModeEanbled,
128+
]) => {
114129
const groupBy = {
130+
key: regexType,
115131
key: regexType,
116132
regexString,
117133
};
118-
const groups =
119-
groupRuns(groupBy, allRuns, runIdToEid, this.expNameByExpId);
134+
const groups = groupRuns(
135+
groupBy,
136+
allRuns,
137+
runIdToEid,
138+
this.expNameByExpId
139+
);
120140
const groupKeyToColorString = new Map<string, string>();
121141
const colorRunPairList: ColorGroup[] = [];
122142

@@ -139,15 +159,18 @@ export class RegexEditDialogContainer {
139159
);
140160
}).pipe(startWith([]));
141161

142-
constructor(
143-
private readonly store: Store<State>,
144-
public dialogRef: MatDialogRef<RegexEditDialogContainer>,
145-
@Inject(MAT_DIALOG_DATA) data: {
146-
experimentIds: string[],
147-
expNameByExpId: Record<string, string>,
148-
}) {
162+
constructor(
163+
private readonly store: Store<State>,
164+
public dialogRef: MatDialogRef<RegexEditDialogContainer>,
165+
@Inject(MAT_DIALOG_DATA)
166+
data: {
167+
experimentIds: string[];
168+
expNameByExpId: Record<string, string>;
169+
}
170+
) {
149171
this.experimentIds = data.experimentIds;
150172
this.expNameByExpId = data.expNameByExpId;
173+
this.expNameByExpId = data.expNameByExpId;
151174

152175
this.runIdToEid$ = combineLatest(
153176
this.experimentIds.map((experimentId) => {
@@ -179,6 +202,7 @@ constructor(
179202
}
180203

181204
onRegexInputOnChange(regexString: string) {
205+
// Whenever regex input changes the subject emits new object.
182206
// Whenever regex input changes the subject emits new object.
183207
this.tentativeRegexString$.next(regexString);
184208
}
@@ -189,16 +213,19 @@ constructor(
189213
}
190214

191215
onSave(): void {
192-
this.groupByRegexString$.pipe(combineLatestWith(this.groupByRegexType$))
193-
.subscribe(([regexString, key]) => {
194-
if (regexString) {
195-
this.store.dispatch(runGroupByChanged({
216+
this.groupByRegexString$
217+
.pipe(combineLatestWith(this.groupByRegexType$))
218+
.subscribe(([regexString, key]) => {
219+
if (regexString) {
220+
this.store.dispatch(
221+
runGroupByChanged({
196222
experimentIds: this.experimentIds,
197223
groupBy: {key, regexString},
198224
expNameByExpId: this.expNameByExpId,
199-
}));
200-
}
201-
});
225+
})
226+
);
227+
}
228+
});
202229
}
203230
}
204231

0 commit comments

Comments
 (0)