pyglove 0.5.0.dev202509250810__tar.gz → 0.5.0.dev202509270808__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pyglove might be problematic. Click here for more details.
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/PKG-INFO +1 -1
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/__init__.py +7 -1
- pyglove-0.5.0.dev202509270808/pyglove/core/monitoring.py +522 -0
- pyglove-0.5.0.dev202509270808/pyglove/core/monitoring_test.py +230 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove.egg-info/PKG-INFO +1 -1
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove.egg-info/SOURCES.txt +2 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/LICENSE +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/README.md +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/__init__.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/coding/__init__.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/coding/errors.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/coding/errors_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/coding/execution.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/coding/execution_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/coding/function_generation.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/coding/function_generation_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/coding/parsing.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/coding/parsing_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/coding/permissions.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/coding/permissions_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/detouring/__init__.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/detouring/class_detour.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/detouring/class_detour_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/geno/__init__.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/geno/base.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/geno/base_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/geno/categorical.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/geno/categorical_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/geno/custom.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/geno/custom_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/geno/deduping.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/geno/deduping_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/geno/dna_generator.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/geno/dna_generator_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/geno/numerical.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/geno/numerical_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/geno/random.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/geno/random_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/geno/space.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/geno/space_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/geno/sweeping.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/geno/sweeping_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/hyper/__init__.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/hyper/base.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/hyper/categorical.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/hyper/categorical_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/hyper/custom.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/hyper/custom_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/hyper/derived.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/hyper/derived_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/hyper/dynamic_evaluation.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/hyper/dynamic_evaluation_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/hyper/evolvable.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/hyper/evolvable_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/hyper/iter.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/hyper/iter_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/hyper/numerical.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/hyper/numerical_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/hyper/object_template.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/hyper/object_template_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/io/__init__.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/io/file_system.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/io/file_system_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/io/sequence.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/io/sequence_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/logging.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/logging_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/patching/__init__.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/patching/object_factory.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/patching/object_factory_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/patching/pattern_based.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/patching/pattern_based_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/patching/rule_based.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/patching/rule_based_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/symbolic/__init__.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/symbolic/base.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/symbolic/base_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/symbolic/boilerplate.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/symbolic/boilerplate_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/symbolic/class_wrapper.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/symbolic/class_wrapper_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/symbolic/compounding.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/symbolic/compounding_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/symbolic/contextual_object.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/symbolic/contextual_object_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/symbolic/dict.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/symbolic/dict_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/symbolic/diff.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/symbolic/diff_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/symbolic/error_info.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/symbolic/error_info_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/symbolic/flags.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/symbolic/flags_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/symbolic/functor.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/symbolic/functor_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/symbolic/inferred.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/symbolic/inferred_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/symbolic/list.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/symbolic/list_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/symbolic/object.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/symbolic/object_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/symbolic/origin.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/symbolic/origin_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/symbolic/pure_symbolic.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/symbolic/ref.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/symbolic/ref_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/symbolic/symbolize.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/symbolic/symbolize_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/tuning/__init__.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/tuning/backend.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/tuning/backend_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/tuning/early_stopping.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/tuning/local_backend.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/tuning/protocols.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/tuning/protocols_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/tuning/sample.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/tuning/sample_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/typing/__init__.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/typing/annotated.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/typing/annotated_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/typing/annotation_conversion.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/typing/annotation_conversion_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/typing/annotation_future_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/typing/callable_ext.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/typing/callable_ext_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/typing/callable_signature.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/typing/callable_signature_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/typing/class_schema.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/typing/class_schema_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/typing/custom_typing.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/typing/inspect.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/typing/inspect_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/typing/json_schema.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/typing/json_schema_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/typing/key_specs.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/typing/key_specs_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/typing/pytype_support.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/typing/type_conversion.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/typing/type_conversion_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/typing/typed_missing.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/typing/typed_missing_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/typing/value_specs.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/typing/value_specs_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/utils/__init__.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/utils/common_traits.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/utils/common_traits_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/utils/contextual.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/utils/contextual_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/utils/docstr_utils.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/utils/docstr_utils_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/utils/error_utils.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/utils/error_utils_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/utils/formatting.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/utils/formatting_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/utils/hierarchical.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/utils/hierarchical_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/utils/json_conversion.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/utils/json_conversion_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/utils/missing.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/utils/missing_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/utils/text_color.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/utils/text_color_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/utils/thread_local.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/utils/thread_local_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/utils/timing.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/utils/timing_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/utils/value_location.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/utils/value_location_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/views/__init__.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/views/base.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/views/base_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/views/html/__init__.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/views/html/base.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/views/html/base_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/views/html/controls/__init__.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/views/html/controls/base.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/views/html/controls/label.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/views/html/controls/label_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/views/html/controls/progress_bar.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/views/html/controls/progress_bar_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/views/html/controls/tab.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/views/html/controls/tab_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/views/html/controls/tooltip.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/views/html/controls/tooltip_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/views/html/tree_view.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/core/views/html/tree_view_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/ext/__init__.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/ext/early_stopping/__init__.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/ext/early_stopping/base.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/ext/early_stopping/base_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/ext/early_stopping/step_wise.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/ext/early_stopping/step_wise_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/ext/evolution/__init__.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/ext/evolution/base.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/ext/evolution/base_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/ext/evolution/hill_climb.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/ext/evolution/hill_climb_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/ext/evolution/mutators.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/ext/evolution/mutators_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/ext/evolution/neat.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/ext/evolution/neat_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/ext/evolution/nsga2.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/ext/evolution/nsga2_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/ext/evolution/recombinators.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/ext/evolution/recombinators_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/ext/evolution/regularized_evolution.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/ext/evolution/regularized_evolution_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/ext/evolution/selectors.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/ext/evolution/selectors_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/ext/evolution/where.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/ext/evolution/where_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/ext/mutfun/__init__.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/ext/mutfun/base.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/ext/mutfun/base_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/ext/mutfun/basic_ops.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/ext/mutfun/basic_ops_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/ext/scalars/__init__.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/ext/scalars/base.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/ext/scalars/base_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/ext/scalars/maths.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/ext/scalars/maths_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/ext/scalars/randoms.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/ext/scalars/randoms_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/ext/scalars/step_wise.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove/ext/scalars/step_wise_test.py +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove.egg-info/dependency_links.txt +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove.egg-info/requires.txt +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/pyglove.egg-info/top_level.txt +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/setup.cfg +0 -0
- {pyglove-0.5.0.dev202509250810 → pyglove-0.5.0.dev202509270808}/setup.py +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright
|
|
1
|
+
# Copyright 2025 The PyGlove Authors
|
|
2
2
|
#
|
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
# you may not use this file except in compliance with the License.
|
|
@@ -353,6 +353,12 @@ from pyglove.core import coding
|
|
|
353
353
|
|
|
354
354
|
from pyglove.core import logging
|
|
355
355
|
|
|
356
|
+
#
|
|
357
|
+
# Symbols from `monitoring.py`.
|
|
358
|
+
#
|
|
359
|
+
|
|
360
|
+
from pyglove.core import monitoring
|
|
361
|
+
|
|
356
362
|
# pylint: enable=g-import-not-at-top
|
|
357
363
|
# pylint: enable=reimported
|
|
358
364
|
# pylint: enable=unused-import
|
|
@@ -0,0 +1,522 @@
|
|
|
1
|
+
# Copyright 2025 The PyGlove Authors
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
"""Pluggable metric systems for monitoring.
|
|
15
|
+
|
|
16
|
+
This module allows PyGlove to plugin metrics to monitor the execution of
|
|
17
|
+
programs.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
import abc
|
|
21
|
+
import collections
|
|
22
|
+
import math
|
|
23
|
+
import threading
|
|
24
|
+
import typing
|
|
25
|
+
from typing import Any, Callable, Dict, List, Optional, Tuple, Type, Union
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
try:
|
|
29
|
+
import numpy # pylint: disable=g-import-not-at-top
|
|
30
|
+
except ImportError:
|
|
31
|
+
numpy = None
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class Metric(metaclass=abc.ABCMeta):
|
|
35
|
+
"""Base class for metrics."""
|
|
36
|
+
|
|
37
|
+
def __init__(
|
|
38
|
+
self,
|
|
39
|
+
namespace: str,
|
|
40
|
+
name: str,
|
|
41
|
+
description: str,
|
|
42
|
+
parameter_definitions: Dict[str, Type[Union[int, str, bool]]]
|
|
43
|
+
) -> None:
|
|
44
|
+
self._namespace = namespace
|
|
45
|
+
self._name = name
|
|
46
|
+
self._description = description
|
|
47
|
+
self._parameter_definitions = parameter_definitions
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def namespace(self) -> str:
|
|
51
|
+
"""Returns the namespace of the metric."""
|
|
52
|
+
return self._namespace
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def name(self) -> str:
|
|
56
|
+
"""Returns the name of the metric."""
|
|
57
|
+
return self._name
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def full_name(self) -> str:
|
|
61
|
+
"""Returns the full name of the metric."""
|
|
62
|
+
return f'{self.namespace}/{self.name}'
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def description(self) -> str:
|
|
66
|
+
"""Returns the description of the metric."""
|
|
67
|
+
return self._description
|
|
68
|
+
|
|
69
|
+
@property
|
|
70
|
+
def parameter_definitions(self) -> Dict[str, Type[Union[int, str, bool]]]:
|
|
71
|
+
"""Returns the parameter definitions of the metric."""
|
|
72
|
+
return self._parameter_definitions
|
|
73
|
+
|
|
74
|
+
def _parameters_key(self, **parameters) -> Tuple[Any, ...]:
|
|
75
|
+
"""Returns the parameters tuple for the metric."""
|
|
76
|
+
for k, t in self._parameter_definitions.items():
|
|
77
|
+
v = parameters.get(k)
|
|
78
|
+
if v is None:
|
|
79
|
+
raise KeyError(
|
|
80
|
+
f'Metric {self.full_name!r}: Parameter {k!r} is required but not '
|
|
81
|
+
f'given.'
|
|
82
|
+
)
|
|
83
|
+
if not isinstance(v, t):
|
|
84
|
+
raise TypeError(
|
|
85
|
+
f'Metric {self.full_name!r}: Parameter {k!r} has type '
|
|
86
|
+
f'{type(v)} but expected type {t}.'
|
|
87
|
+
)
|
|
88
|
+
for k in parameters:
|
|
89
|
+
if k not in self._parameter_definitions:
|
|
90
|
+
raise KeyError(
|
|
91
|
+
f'Metric {self.full_name!r}: Parameter {k!r} is not defined but '
|
|
92
|
+
f'provided. Available parameters: '
|
|
93
|
+
f'{list(self._parameter_definitions.keys())}'
|
|
94
|
+
)
|
|
95
|
+
return tuple(parameters[k] for k in self._parameter_definitions)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class Counter(Metric):
|
|
99
|
+
"""Base class for counters."""
|
|
100
|
+
|
|
101
|
+
@abc.abstractmethod
|
|
102
|
+
def increment(self, delta: int = 1, **parameters) -> int:
|
|
103
|
+
"""Increments the counter by delta and returns the new value.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
delta: The amount to increment the counter by.
|
|
107
|
+
**parameters: Parameters for parameterized counters.
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
The new value of the counter.
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
@abc.abstractmethod
|
|
114
|
+
def value(self, **parameters) -> int:
|
|
115
|
+
"""Returns the value of the counter for the given parameters.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
**parameters: Parameters for parameterized counters.
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
The value of the counter.
|
|
122
|
+
"""
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class Distribution(metaclass=abc.ABCMeta):
|
|
126
|
+
"""Distribution of scalar values."""
|
|
127
|
+
|
|
128
|
+
@property
|
|
129
|
+
@abc.abstractmethod
|
|
130
|
+
def count(self) -> int:
|
|
131
|
+
"""Returns the number of samples in the distribution."""
|
|
132
|
+
|
|
133
|
+
@property
|
|
134
|
+
@abc.abstractmethod
|
|
135
|
+
def sum(self) -> float:
|
|
136
|
+
"""Returns the sum of the distribution."""
|
|
137
|
+
|
|
138
|
+
@property
|
|
139
|
+
@abc.abstractmethod
|
|
140
|
+
def mean(self) -> float:
|
|
141
|
+
"""Returns the mean of the distribution."""
|
|
142
|
+
|
|
143
|
+
@property
|
|
144
|
+
@abc.abstractmethod
|
|
145
|
+
def stddev(self) -> float:
|
|
146
|
+
"""Returns the standard deviation of the distribution."""
|
|
147
|
+
|
|
148
|
+
@property
|
|
149
|
+
def median(self) -> float:
|
|
150
|
+
"""Returns the standard deviation of the distribution."""
|
|
151
|
+
return self.percentile(50)
|
|
152
|
+
|
|
153
|
+
@property
|
|
154
|
+
@abc.abstractmethod
|
|
155
|
+
def variance(self) -> float:
|
|
156
|
+
"""Returns the variance of the distribution."""
|
|
157
|
+
|
|
158
|
+
@abc.abstractmethod
|
|
159
|
+
def percentile(self, n: float) -> float:
|
|
160
|
+
"""Returns the median of the distribution.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
n: The percentile to return. Should be in the range [0, 100].
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
The n-th percentile of the distribution.
|
|
167
|
+
"""
|
|
168
|
+
|
|
169
|
+
@abc.abstractmethod
|
|
170
|
+
def fraction_less_than(self, value: float) -> float:
|
|
171
|
+
"""Returns the fraction of values in the distribution less than value."""
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
class Scalar(Metric):
|
|
175
|
+
"""Base class for scalar values."""
|
|
176
|
+
|
|
177
|
+
@abc.abstractmethod
|
|
178
|
+
def record(self, value: int, **parameters) -> None:
|
|
179
|
+
"""Records a value to the scalar.
|
|
180
|
+
|
|
181
|
+
Args:
|
|
182
|
+
value: The value to record.
|
|
183
|
+
**parameters: Parameters for parameterized scalars.
|
|
184
|
+
"""
|
|
185
|
+
|
|
186
|
+
@abc.abstractmethod
|
|
187
|
+
def distribution(self, **parameters) -> Distribution:
|
|
188
|
+
"""Returns the distribution of the scalar.
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
**parameters: Parameters for parameterized scalars.
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
The distribution of the scalar.
|
|
195
|
+
"""
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
class MetricCollection(metaclass=abc.ABCMeta):
|
|
199
|
+
"""Base class for counter collections."""
|
|
200
|
+
|
|
201
|
+
def __init__(
|
|
202
|
+
self,
|
|
203
|
+
namespace: str,
|
|
204
|
+
default_parameters: Optional[
|
|
205
|
+
Dict[str, Type[Union[int, str, bool]]]
|
|
206
|
+
] = None
|
|
207
|
+
):
|
|
208
|
+
"""Initializes the metric collection.
|
|
209
|
+
|
|
210
|
+
Args:
|
|
211
|
+
namespace: The namespace of the metric collection.
|
|
212
|
+
default_parameters: The default parameters used to create metrics
|
|
213
|
+
if not specified.
|
|
214
|
+
"""
|
|
215
|
+
self._namespace = namespace
|
|
216
|
+
self._default_parameter_definitions = default_parameters or {}
|
|
217
|
+
self._metrics = self._metric_container()
|
|
218
|
+
|
|
219
|
+
@property
|
|
220
|
+
def namespace(self) -> str:
|
|
221
|
+
"""Returns the namespace of the metric collection."""
|
|
222
|
+
return self._namespace
|
|
223
|
+
|
|
224
|
+
def metrics(self) -> List[Metric]:
|
|
225
|
+
"""Returns the names of the metrics."""
|
|
226
|
+
return [m for m in self._metrics.values() if m.namespace == self._namespace]
|
|
227
|
+
|
|
228
|
+
def _metric_container(self) -> dict[str, Metric]:
|
|
229
|
+
"""Returns the container for metrics."""
|
|
230
|
+
return {}
|
|
231
|
+
|
|
232
|
+
def _get_or_create_metric(
|
|
233
|
+
self,
|
|
234
|
+
metric_cls: Type[Metric],
|
|
235
|
+
create_metric_fn: Callable[
|
|
236
|
+
[str, str, Dict[str, Type[Union[int, str, bool]]]],
|
|
237
|
+
Metric
|
|
238
|
+
],
|
|
239
|
+
name: str,
|
|
240
|
+
description: str,
|
|
241
|
+
parameter_definitions: Dict[str, Type[Union[int, str, bool]]]
|
|
242
|
+
) -> Metric:
|
|
243
|
+
"""Gets or creates a metric with the given name."""
|
|
244
|
+
full_name = f'{self._namespace}/{name}'
|
|
245
|
+
metric = self._metrics.get(full_name)
|
|
246
|
+
if metric is not None:
|
|
247
|
+
if not isinstance(metric, metric_cls):
|
|
248
|
+
raise ValueError(
|
|
249
|
+
f'Metric {full_name!r} already exists with a different type '
|
|
250
|
+
f'({type(metric)}).'
|
|
251
|
+
)
|
|
252
|
+
if description != metric.description:
|
|
253
|
+
raise ValueError(
|
|
254
|
+
f'Metric {full_name!r} already exists with a different description '
|
|
255
|
+
f'({metric.description!r}).'
|
|
256
|
+
)
|
|
257
|
+
if parameter_definitions != metric.parameter_definitions:
|
|
258
|
+
raise ValueError(
|
|
259
|
+
f'Metric {full_name!r} already exists with different parameter '
|
|
260
|
+
f'definitions ({metric.parameter_definitions!r}).'
|
|
261
|
+
)
|
|
262
|
+
else:
|
|
263
|
+
metric = create_metric_fn(name, description, parameter_definitions)
|
|
264
|
+
self._metrics[full_name] = metric
|
|
265
|
+
return metric
|
|
266
|
+
|
|
267
|
+
def get_counter(
|
|
268
|
+
self,
|
|
269
|
+
name: str,
|
|
270
|
+
description: str,
|
|
271
|
+
parameters: Optional[Dict[str, Type[Union[int, str, bool]]]] = None,
|
|
272
|
+
**kwargs
|
|
273
|
+
) -> Counter:
|
|
274
|
+
"""Gets or creates a counter with the given name.
|
|
275
|
+
|
|
276
|
+
Args:
|
|
277
|
+
name: The name of the counter.
|
|
278
|
+
description: The description of the counter.
|
|
279
|
+
parameters: The definitions of the parameters for the counter.
|
|
280
|
+
`default_parameters` from the collection will be used if not specified.
|
|
281
|
+
**kwargs: Additional arguments for creating the counter.
|
|
282
|
+
|
|
283
|
+
Returns:
|
|
284
|
+
The counter with the given name.
|
|
285
|
+
"""
|
|
286
|
+
if parameters is None:
|
|
287
|
+
parameters = self._default_parameter_definitions
|
|
288
|
+
return typing.cast(
|
|
289
|
+
Counter, self._get_or_create_metric(
|
|
290
|
+
Counter, self._create_counter, name, description, parameters,
|
|
291
|
+
**kwargs
|
|
292
|
+
)
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
@abc.abstractmethod
|
|
296
|
+
def _create_counter(
|
|
297
|
+
self,
|
|
298
|
+
name: str,
|
|
299
|
+
description: str,
|
|
300
|
+
parameter_definitions: Dict[str, Type[Union[int, str, bool]]],
|
|
301
|
+
**kwargs
|
|
302
|
+
) -> Counter:
|
|
303
|
+
"""Creates a counter with the given name."""
|
|
304
|
+
|
|
305
|
+
def get_scalar(
|
|
306
|
+
self,
|
|
307
|
+
name: str,
|
|
308
|
+
description: str,
|
|
309
|
+
parameters: Optional[Dict[str, Type[Union[int, str, bool]]]] = None,
|
|
310
|
+
**kwargs
|
|
311
|
+
) -> Scalar:
|
|
312
|
+
"""Gets or creates a scalar with the given name.
|
|
313
|
+
|
|
314
|
+
Args:
|
|
315
|
+
name: The name of the counter.
|
|
316
|
+
description: The description of the counter.
|
|
317
|
+
parameters: The definitions of the parameters for the counter.
|
|
318
|
+
`default_parameters` from the collection will be used if not specified.
|
|
319
|
+
**kwargs: Additional arguments for creating the scalar.
|
|
320
|
+
|
|
321
|
+
Returns:
|
|
322
|
+
The counter with the given name.
|
|
323
|
+
"""
|
|
324
|
+
if parameters is None:
|
|
325
|
+
parameters = self._default_parameter_definitions
|
|
326
|
+
return typing.cast(
|
|
327
|
+
Scalar,
|
|
328
|
+
self._get_or_create_metric(
|
|
329
|
+
Scalar, self._create_scalar, name, description, parameters, **kwargs
|
|
330
|
+
)
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
@abc.abstractmethod
|
|
334
|
+
def _create_scalar(
|
|
335
|
+
self,
|
|
336
|
+
name: str,
|
|
337
|
+
description: str,
|
|
338
|
+
parameter_definitions: Dict[str, Type[Union[int, str, bool]]],
|
|
339
|
+
**kwargs
|
|
340
|
+
) -> Scalar:
|
|
341
|
+
"""Creates a counter with the given name."""
|
|
342
|
+
|
|
343
|
+
#
|
|
344
|
+
# InMemoryMetricCollection.
|
|
345
|
+
#
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
class _InMemoryCounter(Counter):
|
|
349
|
+
"""In-memory counter."""
|
|
350
|
+
|
|
351
|
+
def __init__(self, *args, **kwargs):
|
|
352
|
+
super().__init__(*args, **kwargs)
|
|
353
|
+
self._counter = collections.defaultdict(int)
|
|
354
|
+
self._lock = threading.Lock()
|
|
355
|
+
|
|
356
|
+
def increment(self, delta: int = 1, **parameters) -> int:
|
|
357
|
+
"""Increments the counter by delta and returns the new value."""
|
|
358
|
+
parameters_key = self._parameters_key(**parameters)
|
|
359
|
+
with self._lock:
|
|
360
|
+
value = self._counter[parameters_key]
|
|
361
|
+
value += delta
|
|
362
|
+
self._counter[parameters_key] = value
|
|
363
|
+
return value
|
|
364
|
+
|
|
365
|
+
def value(self, **parameters) -> int:
|
|
366
|
+
"""Returns the value of the counter based on the given parameters."""
|
|
367
|
+
return self._counter[self._parameters_key(**parameters)]
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
class _InMemoryScalar(Scalar):
|
|
371
|
+
"""In-memory scalar."""
|
|
372
|
+
|
|
373
|
+
def __init__(self, *args, window_size: int = 1024 * 1024, **kwargs):
|
|
374
|
+
super().__init__(*args, **kwargs)
|
|
375
|
+
self._window_size = window_size
|
|
376
|
+
self._distributions = collections.defaultdict(
|
|
377
|
+
lambda: _InMemoryDistribution(self._window_size)
|
|
378
|
+
)
|
|
379
|
+
self._lock = threading.Lock()
|
|
380
|
+
|
|
381
|
+
def record(self, value: Any, **parameters) -> None:
|
|
382
|
+
"""Records a value to the scalar."""
|
|
383
|
+
parameters_key = self._parameters_key(**parameters)
|
|
384
|
+
self._distributions[parameters_key].add(value)
|
|
385
|
+
|
|
386
|
+
def distribution(self, **parameters) -> Distribution:
|
|
387
|
+
"""Returns the distribution of the scalar."""
|
|
388
|
+
parameters_key = self._parameters_key(**parameters)
|
|
389
|
+
return self._distributions[parameters_key]
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
class _InMemoryDistribution(Distribution):
|
|
393
|
+
"""In memory distribution of scalar values."""
|
|
394
|
+
|
|
395
|
+
def __init__(self, window_size: int = 1024 * 1024):
|
|
396
|
+
self._window_size = window_size
|
|
397
|
+
self._data = []
|
|
398
|
+
self._sum = 0.0
|
|
399
|
+
self._square_sum = 0.0
|
|
400
|
+
self._count = 0
|
|
401
|
+
self._lock = threading.Lock()
|
|
402
|
+
|
|
403
|
+
def add(self, value: int):
|
|
404
|
+
"""Adds a value to the distribution."""
|
|
405
|
+
with self._lock:
|
|
406
|
+
if len(self._data) == self._window_size:
|
|
407
|
+
x = self._data.pop(0)
|
|
408
|
+
self._sum -= x
|
|
409
|
+
self._count -= 1
|
|
410
|
+
self._square_sum -= x ** 2
|
|
411
|
+
|
|
412
|
+
self._data.append(value)
|
|
413
|
+
self._count += 1
|
|
414
|
+
self._sum += value
|
|
415
|
+
self._square_sum += value ** 2
|
|
416
|
+
|
|
417
|
+
@property
|
|
418
|
+
def count(self) -> int:
|
|
419
|
+
"""Returns the number of samples in the distribution."""
|
|
420
|
+
return self._count
|
|
421
|
+
|
|
422
|
+
@property
|
|
423
|
+
def sum(self) -> float:
|
|
424
|
+
"""Returns the sum of the distribution."""
|
|
425
|
+
return self._sum
|
|
426
|
+
|
|
427
|
+
@property
|
|
428
|
+
def mean(self) -> float:
|
|
429
|
+
"""Returns the mean of the distribution."""
|
|
430
|
+
return self._sum / self._count if self._count else 0.0
|
|
431
|
+
|
|
432
|
+
@property
|
|
433
|
+
def stddev(self) -> float:
|
|
434
|
+
"""Returns the standard deviation of the distribution."""
|
|
435
|
+
return math.sqrt(self.variance)
|
|
436
|
+
|
|
437
|
+
@property
|
|
438
|
+
def variance(self) -> float:
|
|
439
|
+
"""Returns the variance of the distribution."""
|
|
440
|
+
if self._count < 2:
|
|
441
|
+
return 0.0
|
|
442
|
+
return self._square_sum / self._count - self.mean ** 2
|
|
443
|
+
|
|
444
|
+
def percentile(self, n: float) -> float:
|
|
445
|
+
"""Returns the median of the distribution."""
|
|
446
|
+
if n < 0 or n > 100:
|
|
447
|
+
raise ValueError(f'Percentile {n} is not in the range [0, 100].')
|
|
448
|
+
|
|
449
|
+
if self._count == 0:
|
|
450
|
+
return 0.0
|
|
451
|
+
|
|
452
|
+
if numpy is not None:
|
|
453
|
+
return numpy.percentile(self._data, n) # pytype: disable=attribute-error
|
|
454
|
+
|
|
455
|
+
sorted_data = sorted(self._data)
|
|
456
|
+
index = (n / 100) * (len(sorted_data) - 1)
|
|
457
|
+
if index % 1 == 0:
|
|
458
|
+
return sorted_data[int(index)]
|
|
459
|
+
else:
|
|
460
|
+
# Interpolate the value at the given percentile.
|
|
461
|
+
lower_index = int(index)
|
|
462
|
+
fraction = index - lower_index
|
|
463
|
+
|
|
464
|
+
# Get the values at the two surrounding integer indices
|
|
465
|
+
lower_value = sorted_data[lower_index]
|
|
466
|
+
upper_value = sorted_data[lower_index + 1]
|
|
467
|
+
return lower_value + fraction * (upper_value - lower_value)
|
|
468
|
+
|
|
469
|
+
def fraction_less_than(self, value: float) -> float:
|
|
470
|
+
"""Returns the fraction of values in the distribution less than value."""
|
|
471
|
+
if self._count == 0:
|
|
472
|
+
return 0.0
|
|
473
|
+
with self._lock:
|
|
474
|
+
return len([x for x in self._data if x < value]) / self._count
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
class InMemoryMetricCollection(MetricCollection):
|
|
478
|
+
"""In-memory counter."""
|
|
479
|
+
|
|
480
|
+
def _create_counter(
|
|
481
|
+
self,
|
|
482
|
+
name: str,
|
|
483
|
+
description: str,
|
|
484
|
+
parameter_definitions: Dict[str, Type[Union[int, str, bool]]],
|
|
485
|
+
**kwargs
|
|
486
|
+
) -> Counter:
|
|
487
|
+
return _InMemoryCounter(
|
|
488
|
+
self._namespace, name, description, parameter_definitions
|
|
489
|
+
)
|
|
490
|
+
|
|
491
|
+
def _create_scalar(
|
|
492
|
+
self,
|
|
493
|
+
name: str,
|
|
494
|
+
description: str,
|
|
495
|
+
parameter_definitions: Dict[str, Type[Union[int, str, bool]]],
|
|
496
|
+
*,
|
|
497
|
+
window_size: int = 1024 * 1024,
|
|
498
|
+
**kwargs
|
|
499
|
+
) -> Scalar:
|
|
500
|
+
return _InMemoryScalar(
|
|
501
|
+
self._namespace, name, description, parameter_definitions,
|
|
502
|
+
window_size=window_size, **kwargs
|
|
503
|
+
)
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
_METRIC_COLLECTION_CLS = InMemoryMetricCollection # pylint: disable=invalid-name
|
|
507
|
+
|
|
508
|
+
|
|
509
|
+
def metric_collection(namespace: str, **kwargs) -> MetricCollection:
|
|
510
|
+
"""Creates a metric collection."""
|
|
511
|
+
return _METRIC_COLLECTION_CLS(namespace, **kwargs)
|
|
512
|
+
|
|
513
|
+
|
|
514
|
+
def set_default_metric_collection_cls(cls: Type[MetricCollection]) -> None:
|
|
515
|
+
"""Sets the default metric collection class."""
|
|
516
|
+
global _METRIC_COLLECTION_CLS
|
|
517
|
+
_METRIC_COLLECTION_CLS = cls
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
def default_metric_collection_cls() -> Type[MetricCollection]:
|
|
521
|
+
"""Returns the default metric collection class."""
|
|
522
|
+
return _METRIC_COLLECTION_CLS
|