Skip to content

Commit d190899

Browse files
authored
HParams: Add new state property to store hparam and metric filters (#6553)
## Motivation for features / changes Now that the filter dialog is merged #6493 we need to add support for filtering by hparams. However, #6544 changed the way hparam values are read. This change adds a new property to state in which to store dashboard related hparam and metric filters. See #6488 for the WIP integration with the runs_table_container + tb_data_table -> common_selectors + hparam_selectors.
1 parent 31c61d5 commit d190899

File tree

10 files changed

+332
-7
lines changed

10 files changed

+332
-7
lines changed

tensorboard/webapp/hparams/_redux/BUILD

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ tf_ts_library(
1010
"utils.ts",
1111
],
1212
deps = [
13+
":types",
1314
"//tensorboard/webapp/hparams:types",
1415
],
1516
)
@@ -47,6 +48,7 @@ tf_ts_library(
4748
"hparams_actions.ts",
4849
],
4950
deps = [
51+
":types",
5052
"//tensorboard/webapp/hparams:types",
5153
"@npm//@ngrx/store",
5254
],
@@ -155,6 +157,7 @@ tf_ts_library(
155157
":hparams_reducers",
156158
":hparams_selectors",
157159
":testing",
160+
":types",
158161
":utils",
159162
"//tensorboard/webapp:app_state",
160163
"//tensorboard/webapp:selectors",

tensorboard/webapp/hparams/_redux/hparams_actions.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
HparamAndMetricSpec,
2323
SessionGroup,
2424
} from '../types';
25+
import {HparamFilter, MetricFilter} from './types';
2526

2627
export const hparamsDiscreteHparamFilterChanged = createAction(
2728
'[Hparams] Hparams Discrete Hparam Filter Changed',
@@ -62,3 +63,13 @@ export const hparamsFetchSessionGroupsSucceeded = createAction(
6263
sessionGroups: SessionGroup[];
6364
}>()
6465
);
66+
67+
export const dashboardHparamFilterAdded = createAction(
68+
'[Hparams] Dashboard Hparam Filter Added',
69+
props<{name: string; filter: HparamFilter}>()
70+
);
71+
72+
export const dashboardMetricFilterAdded = createAction(
73+
'[Hparams] Dashboard Metric Filter Added',
74+
props<{name: string; filter: MetricFilter}>()
75+
);

tensorboard/webapp/hparams/_redux/hparams_reducers.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ const initialState: HparamsState = {
3737
metrics: [],
3838
},
3939
dashboardSessionGroups: [],
40+
dashboardFilters: {
41+
hparams: new Map(),
42+
metrics: new Map(),
43+
},
4044
};
4145

4246
const reducer: ActionReducer<HparamsState, Action> = createReducer(
@@ -369,6 +373,30 @@ const reducer: ActionReducer<HparamsState, Action> = createReducer(
369373
dashboardSpecs: nextDashboardSpecs,
370374
dashboardSessionGroups: nextDashboardSessionGroups,
371375
};
376+
}),
377+
on(actions.dashboardHparamFilterAdded, (state, action) => {
378+
const nextHparamFilters = new Map(state.dashboardFilters.hparams);
379+
nextHparamFilters.set(action.name, action.filter);
380+
381+
return {
382+
...state,
383+
dashboardFilters: {
384+
...state.dashboardFilters,
385+
hparams: nextHparamFilters,
386+
},
387+
};
388+
}),
389+
on(actions.dashboardMetricFilterAdded, (state, action) => {
390+
const nextMetricFilters = new Map(state.dashboardFilters.metrics);
391+
nextMetricFilters.set(action.name, action.filter);
392+
393+
return {
394+
...state,
395+
dashboardFilters: {
396+
...state.dashboardFilters,
397+
metrics: nextMetricFilters,
398+
},
399+
};
372400
})
373401
);
374402

tensorboard/webapp/hparams/_redux/hparams_reducers_test.ts

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,4 +630,126 @@ describe('hparams/_redux/hparams_reducers_test', () => {
630630
expect(state2.dashboardSessionGroups).toEqual([mockSessionGroup]);
631631
});
632632
});
633+
634+
describe('dashboardHparamFilterAdded', () => {
635+
it('adds entry dashboardFilters', () => {
636+
const state = buildHparamsState({
637+
dashboardFilters: {
638+
hparams: new Map([
639+
[
640+
'hparam2',
641+
{
642+
type: DomainType.INTERVAL,
643+
includeUndefined: true,
644+
minValue: 2,
645+
maxValue: 20,
646+
filterLowerBound: 2,
647+
filterUpperBound: 20,
648+
},
649+
],
650+
]),
651+
},
652+
});
653+
const state2 = reducers(
654+
state,
655+
actions.dashboardHparamFilterAdded({
656+
name: 'hparam1',
657+
filter: {
658+
type: DomainType.DISCRETE,
659+
includeUndefined: true,
660+
filterValues: [5],
661+
possibleValues: [5, 7, 8],
662+
},
663+
})
664+
);
665+
expect(state2.dashboardFilters).toEqual({
666+
hparams: new Map([
667+
[
668+
'hparam1',
669+
{
670+
type: DomainType.DISCRETE,
671+
includeUndefined: true,
672+
filterValues: [5],
673+
possibleValues: [5, 7, 8],
674+
},
675+
],
676+
[
677+
'hparam2',
678+
{
679+
type: DomainType.INTERVAL,
680+
includeUndefined: true,
681+
minValue: 2,
682+
maxValue: 20,
683+
filterLowerBound: 2,
684+
filterUpperBound: 20,
685+
},
686+
],
687+
]),
688+
metrics: new Map(),
689+
});
690+
});
691+
});
692+
693+
describe('dashboardMetricFilterAdded', () => {
694+
it('adds entry dashboardFilters', () => {
695+
const state = buildHparamsState({
696+
dashboardFilters: {
697+
metrics: new Map([
698+
[
699+
'metric 2',
700+
{
701+
type: DomainType.INTERVAL,
702+
includeUndefined: true,
703+
minValue: 1,
704+
maxValue: 50,
705+
filterLowerValue: 1,
706+
filterUpperValue: 51,
707+
},
708+
],
709+
]),
710+
},
711+
});
712+
const state2 = reducers(
713+
state,
714+
actions.dashboardMetricFilterAdded({
715+
name: 'metric 1',
716+
filter: {
717+
type: DomainType.INTERVAL,
718+
includeUndefined: true,
719+
minValue: -2,
720+
maxValue: 42,
721+
filterLowerValue: -2,
722+
filterUpperValue: 40,
723+
},
724+
})
725+
);
726+
expect(state2.dashboardFilters).toEqual({
727+
hparams: new Map(),
728+
metrics: new Map([
729+
[
730+
'metric 1',
731+
{
732+
type: DomainType.INTERVAL,
733+
includeUndefined: true,
734+
minValue: -2,
735+
maxValue: 42,
736+
filterLowerValue: -2,
737+
filterUpperValue: 40,
738+
},
739+
],
740+
[
741+
'metric 2',
742+
{
743+
type: DomainType.INTERVAL,
744+
includeUndefined: true,
745+
minValue: 1,
746+
maxValue: 50,
747+
filterLowerValue: 1,
748+
filterUpperValue: 51,
749+
},
750+
],
751+
]),
752+
});
753+
});
754+
});
633755
});

tensorboard/webapp/hparams/_redux/hparams_selectors.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,12 @@ import {
2121
RunToHparamsAndMetrics,
2222
} from '../types';
2323
import {combineHparamAndMetricSpecs} from './hparams_selectors_utils';
24-
import {HparamsState, HPARAMS_FEATURE_KEY} from './types';
24+
import {HparamsState, HPARAMS_FEATURE_KEY, HparamFilter} from './types';
2525
import {
2626
combineDefaultHparamFilters,
2727
combineDefaultMetricFilters,
2828
getIdFromExperimentIds,
29+
hparamSpecToDefaultFilter,
2930
} from './utils';
3031

3132
const getHparamsState =
@@ -232,3 +233,30 @@ export const getDashboardRunsToHparamsAndMetrics = createSelector(
232233
return runToHparamsAndMetrics;
233234
}
234235
);
236+
237+
export const getDashboardDefaultHparamFilters = createSelector(
238+
getDashboardHparamsAndMetricsSpecs,
239+
(specs): Map<string, HparamFilter> => {
240+
const hparams = new Map(
241+
specs.hparams.map((hparamSpec) => {
242+
return [hparamSpec.name, hparamSpecToDefaultFilter(hparamSpec)];
243+
})
244+
);
245+
246+
return hparams;
247+
}
248+
);
249+
250+
export const getDashboardHparamFilterMap = createSelector(
251+
getHparamsState,
252+
(state) => {
253+
return state.dashboardFilters.hparams;
254+
}
255+
);
256+
257+
export const getDashboardMetricsFilterMap = createSelector(
258+
getHparamsState,
259+
(state) => {
260+
return state.dashboardFilters.metrics;
261+
}
262+
);

tensorboard/webapp/hparams/_redux/hparams_selectors_test.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ See the License for the specific language governing permissions and
1313
limitations under the License.
1414
==============================================================================*/
1515

16+
import {DomainType} from '../types';
1617
import * as selectors from './hparams_selectors';
1718
import {
1819
buildDiscreteFilter,
@@ -701,6 +702,58 @@ describe('hparams/_redux/hparams_selectors_test', () => {
701702
});
702703
});
703704

705+
describe('#getDashboardDefaultHparamFilters', () => {
706+
it('generates default filters for all hparam specs', () => {
707+
const state = buildStateFromHparamsState(
708+
buildHparamsState({
709+
dashboardSpecs: {
710+
hparams: [
711+
buildHparamSpec({
712+
name: 'interval hparam',
713+
domain: {
714+
type: DomainType.INTERVAL,
715+
minValue: 2,
716+
maxValue: 5,
717+
},
718+
}),
719+
buildHparamSpec({
720+
name: 'discrete hparam',
721+
domain: {
722+
type: DomainType.DISCRETE,
723+
values: [2, 4, 6, 8],
724+
},
725+
}),
726+
],
727+
},
728+
})
729+
);
730+
expect(selectors.getDashboardDefaultHparamFilters(state)).toEqual(
731+
new Map([
732+
[
733+
'interval hparam',
734+
{
735+
type: DomainType.INTERVAL,
736+
includeUndefined: true,
737+
minValue: 2,
738+
maxValue: 5,
739+
filterLowerValue: 2,
740+
filterUpperValue: 5,
741+
},
742+
],
743+
[
744+
'discrete hparam',
745+
{
746+
type: DomainType.DISCRETE,
747+
includeUndefined: true,
748+
possibleValues: [2, 4, 6, 8],
749+
filterValues: [2, 4, 6, 8],
750+
},
751+
],
752+
])
753+
);
754+
});
755+
});
756+
704757
it('does not use default filters when includeDefaults is false', () => {
705758
const state = buildStateFromHparamsState(
706759
buildHparamsState({

tensorboard/webapp/hparams/_redux/testing.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ export function buildHparamsState(
8585
metrics: overrides.dashboardSpecs?.metrics ?? [],
8686
},
8787
dashboardSessionGroups: overrides.dashboardSessionGroups ?? [],
88+
dashboardFilters: {
89+
hparams: overrides.dashboardFilters?.hparams ?? new Map(),
90+
metrics: overrides.dashboardFilters?.metrics ?? new Map(),
91+
},
8892
} as HparamsState;
8993
}
9094

tensorboard/webapp/hparams/_redux/types.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,17 @@ import {
2121
SessionGroup,
2222
} from '../_types';
2323

24+
export type HparamFilter = DiscreteFilter | IntervalFilter;
25+
export type MetricFilter = IntervalFilter;
26+
2427
export interface HparamsMetricsAndFilters {
2528
hparam: {
2629
specs: HparamSpec[];
27-
defaultFilters: Map<string, DiscreteFilter | IntervalFilter>;
30+
defaultFilters: Map<string, HparamFilter>;
2831
};
2932
metric: {
3033
specs: MetricSpec[];
31-
defaultFilters: Map<string, IntervalFilter>;
34+
defaultFilters: Map<string, MetricFilter>;
3235
};
3336
}
3437

@@ -47,6 +50,10 @@ export interface HparamsState {
4750
specs: ExperimentToHparams;
4851
dashboardSpecs: HparamAndMetricSpec;
4952
dashboardSessionGroups: SessionGroup[];
53+
dashboardFilters: {
54+
hparams: Map<string, HparamFilter>;
55+
metrics: Map<string, MetricFilter>;
56+
};
5057
/**
5158
* RATIONALE: we do not use the NamespaceContextedState because of the following reasons.
5259
* - RunsTable which uses the state renders both on the dashboard view and the
@@ -62,8 +69,8 @@ export interface HparamsState {
6269
*/
6370
filters: {
6471
[id: string]: {
65-
hparams: Map<string, DiscreteFilter | IntervalFilter>;
66-
metrics: Map<string, IntervalFilter>;
72+
hparams: Map<string, HparamFilter>;
73+
metrics: Map<string, MetricFilter>;
6774
};
6875
};
6976
}

0 commit comments

Comments
 (0)