Skip to content

Commit ed8cb96

Browse files
xingyousongcopybara-github
authored andcommitted
Add AsExternalType to OSS Vizier proto following #1185
PiperOrigin-RevId: 691261698
1 parent 64b0220 commit ed8cb96

File tree

4 files changed

+108
-8
lines changed

4 files changed

+108
-8
lines changed

vizier/_src/pyvizier/oss/proto_converters.py

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535

3636
ScaleType = parameter_config.ScaleType
3737
_ScaleTypePb2 = study_pb2.StudySpec.ParameterSpec.ScaleType
38+
ExternalType = parameter_config.ExternalType
39+
_ExternalTypePb2 = study_pb2.StudySpec.ParameterSpec.ExternalType
3840
ParameterType = parameter_config.ParameterType
3941
MonotypeParameterSequence = parameter_config.MonotypeParameterSequence
4042

@@ -89,6 +91,26 @@ def from_proto(cls, proto: _ScaleTypePb2) -> ScaleType:
8991
return cls._proto_to_pyvizier[proto]
9092

9193

94+
class _ExternalTypeMap:
95+
"""Proto converter for external type."""
96+
97+
_pyvizier_to_proto = dict([
98+
(ExternalType.INTERNAL, _ExternalTypePb2.AS_INTERNAL),
99+
(ExternalType.BOOLEAN, _ExternalTypePb2.AS_BOOLEAN),
100+
(ExternalType.INTEGER, _ExternalTypePb2.AS_INTEGER),
101+
(ExternalType.FLOAT, _ExternalTypePb2.AS_FLOAT),
102+
])
103+
_proto_to_pyvizier = {v: k for k, v in _pyvizier_to_proto.items()}
104+
105+
@classmethod
106+
def to_proto(cls, pyvizier: ExternalType) -> _ExternalTypePb2:
107+
return cls._pyvizier_to_proto[pyvizier]
108+
109+
@classmethod
110+
def from_proto(cls, proto: _ExternalTypePb2) -> ExternalType:
111+
return cls._proto_to_pyvizier[proto]
112+
113+
92114
class ParameterConfigConverter:
93115
"""Converter for ParameterConfig."""
94116

@@ -173,7 +195,8 @@ def from_proto(
173195
Raises:
174196
ValueError: See the "strict_validtion" arg documentation.
175197
"""
176-
feasible_values = []
198+
bounds = None
199+
feasible_values = None
177200
oneof_name = proto.WhichOneof('parameter_value_spec')
178201
if oneof_name == 'integer_value_spec':
179202
bounds = (
@@ -210,6 +233,10 @@ def from_proto(
210233
if proto.scale_type:
211234
scale_type = _ScaleTypeMap.from_proto(proto.scale_type)
212235

236+
external_type = None
237+
if proto.external_type:
238+
external_type = _ExternalTypeMap.from_proto(proto.external_type)
239+
213240
try:
214241
config = parameter_config.ParameterConfig.factory(
215242
name=proto.parameter_id,
@@ -218,6 +245,7 @@ def from_proto(
218245
children=children,
219246
scale_type=scale_type,
220247
default_value=default_value,
248+
external_type=external_type,
221249
)
222250
except ValueError as e:
223251
raise ValueError(
@@ -258,10 +286,8 @@ def _set_child_parameter_configs(
258286
parent_proto.ClearField('conditional_parameter_specs')
259287
for child_pair in children:
260288
if len(child_pair) != 2:
261-
raise ValueError(
262-
"""Each element in children must be a tuple of
263-
(Sequence of valid parent values, ParameterConfig)"""
264-
)
289+
raise ValueError("""Each element in children must be a tuple of
290+
(Sequence of valid parent values, ParameterConfig)""")
265291

266292
logging.debug(
267293
'_set_child_parameter_configs: parent_proto=%s, children=%s',
@@ -316,6 +342,8 @@ def to_proto(
316342
proto.scale_type = _ScaleTypeMap.to_proto(pc.scale_type)
317343
if pc.default_value is not None:
318344
cls._set_default_value(proto, pc.default_value)
345+
if pc.external_type is not None:
346+
proto.external_type = _ExternalTypeMap.to_proto(pc.external_type)
319347

320348
cls._set_child_parameter_configs(proto, pc)
321349
return proto

vizier/_src/pyvizier/oss/proto_converters_test.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,7 @@
1717
"""Tests for proto_converters."""
1818

1919
from absl import logging
20-
2120
import attr
22-
2321
from vizier._src.pyvizier.oss import proto_converters
2422
from vizier._src.pyvizier.pythia import study
2523
from vizier._src.pyvizier.shared import parameter_config as pc
@@ -365,6 +363,7 @@ def testDiscreteConfigToProto(self):
365363
'name',
366364
feasible_values=feasible_values,
367365
scale_type=pc.ScaleType.LOG,
366+
external_type=pc.ExternalType.INTEGER,
368367
default_value=2,
369368
)
370369

@@ -376,6 +375,10 @@ def testDiscreteConfigToProto(self):
376375
proto.scale_type,
377376
study_pb2.StudySpec.ParameterSpec.ScaleType.UNIT_LOG_SCALE,
378377
)
378+
self.assertEqual(
379+
proto.external_type,
380+
study_pb2.StudySpec.ParameterSpec.ExternalType.AS_INTEGER,
381+
)
379382

380383

381384
class ParameterConfigConverterFromProtoTest(absltest.TestCase):

vizier/_src/pyvizier/oss/study_config_test.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,57 @@ def testPyTrialToDict(self):
384384
self.assertIsInstance(parameters['batch_size'], int)
385385
self.assertIsInstance(parameters['floating_point_param'], float)
386386

387+
def testTrialToDictWithExternalType(self):
388+
"""Test conversion when external types are not specified."""
389+
proto = study_pb2.StudySpec()
390+
proto.parameters.add(
391+
parameter_id='learning_rate',
392+
double_value_spec=study_pb2.StudySpec.ParameterSpec.DoubleValueSpec(
393+
min_value=1e-4, max_value=0.1),
394+
scale_type=study_pb2.StudySpec.ParameterSpec.ScaleType.UNIT_LOG_SCALE)
395+
proto.parameters.add(
396+
parameter_id='batch_size',
397+
discrete_value_spec=study_pb2.StudySpec.ParameterSpec.DiscreteValueSpec(
398+
values=[1.0, 2.0, 4.0, 8.0, 16.0, 32.0, 64.0, 128.0]),
399+
external_type=study_pb2.StudySpec.ParameterSpec.ExternalType.AS_INTEGER)
400+
proto.parameters.add(
401+
parameter_id='training_steps',
402+
discrete_value_spec=study_pb2.StudySpec.ParameterSpec.DiscreteValueSpec(
403+
values=[1000.0, 10000.0]),
404+
external_type=study_pb2.StudySpec.ParameterSpec.ExternalType.AS_INTEGER)
405+
proto.observation_noise = study_pb2.StudySpec.ObservationNoise.HIGH
406+
proto.metrics.add(
407+
metric_id='loss', goal=study_pb2.StudySpec.MetricSpec.MINIMIZE)
408+
409+
trial_proto = study_pb2.Trial()
410+
trial_proto.id = str(1)
411+
trial_proto.parameters.add(
412+
parameter_id='batch_size', value=struct_pb2.Value(number_value=128.0))
413+
trial_proto.parameters.add(
414+
parameter_id='learning_rate',
415+
value=struct_pb2.Value(number_value=1.2137854406366652E-4))
416+
trial_proto.parameters.add(
417+
parameter_id='training_steps',
418+
value=struct_pb2.Value(number_value=10000.0))
419+
420+
py_study_config = vz.StudyConfig.from_proto(proto)
421+
self.assertEqual(
422+
py_study_config.observation_noise, vz.ObservationNoise.HIGH
423+
)
424+
parameters = py_study_config.trial_parameters(trial_proto)
425+
self.assertEqual(
426+
py_study_config.observation_noise, vz.ObservationNoise.HIGH
427+
)
428+
expected = {
429+
'batch_size': 128,
430+
'learning_rate': 1.2137854406366652E-4,
431+
'training_steps': 10000.0
432+
}
433+
self.assertEqual(expected, parameters)
434+
self.assertIsInstance(parameters['learning_rate'], float)
435+
self.assertIsInstance(parameters['batch_size'], int)
436+
self.assertIsInstance(parameters['training_steps'], int)
437+
387438
def testTrialToDictWithoutExternalType(self):
388439
"""Test conversion when external types are not specified."""
389440
proto = study_pb2.StudySpec()

vizier/_src/service/study.proto

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,24 @@ message StudySpec {
289289
// Leave unset for `CATEGORICAL` parameters.
290290
ScaleType scale_type = 6;
291291

292+
// This is a place where the Vizier client can note the representation it
293+
// presents to its callers.
294+
// e.g. boolean can be represented inside Vizier in several ways (e.g.
295+
// CATEGORICAL, INTEGER, or DOUBLE). Or, to represent a python range like
296+
// range(10, 100, 10), you need to use an internal DOUBLE representation and
297+
// use the external AS_INTEGER representation.
298+
//
299+
// NOTE: This field is not examined or modified by the Vizier service.
300+
// NOTE: Not all combinations of ExternalType and ParameterType make sense.
301+
enum ExternalType {
302+
AS_INTERNAL = 0;
303+
AS_BOOLEAN = 1;
304+
AS_INTEGER = 2;
305+
AS_FLOAT = 3;
306+
}
307+
308+
ExternalType external_type = 7;
309+
292310
// Represents a parameter spec with condition from its parent parameter.
293311
message ConditionalParameterSpec {
294312
// The spec for a conditional parameter.
@@ -351,7 +369,7 @@ message StudySpec {
351369
// See pyvizier.Algorithm for a list of native Vizier algorithms.
352370
string algorithm = 3;
353371

354-
// Use the deault early stopping policy.
372+
// Use the default early stopping policy.
355373
reserved 4, 5, 8;
356374
message DefaultEarlyStoppingSpec {}
357375

0 commit comments

Comments
 (0)