This repository was archived by the owner on Jul 10, 2026. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 471
Support dynamic tempo
#3383
Merged
Merged
Support dynamic tempo
#3383
Changes from 20 commits
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
c1056a3
update pallets.py
basfroman 9ca17f9
add new extrinsics
basfroman fb34714
add epoch schedule utils
basfroman 3a3667a
add constants with TODO (will be revealed from the chain later)
basfroman b96cbd7
improve subnet_hyperparameters.py module + .utils.Self
basfroman 524e599
add `.types.EpochScheduleState` class
basfroman 69ef3ce
use `get_encrypted_commit_v2` in `commit_timelocked_weights_extrinsic`
basfroman 408aea3
update Async/Subtensor classes
basfroman 4d4565e
update extras
basfroman c798dbb
add new unit tests
basfroman 12e19bb
add e2e tests for dynamic tempo extrinsics
basfroman b027741
ooops, update `test_commit_timelocked_weights_extrinsic` e2e tests
basfroman dce211f
fix `test_metagraph_info_async`
basfroman 327d1da
bumping version
basfroman f3ae276
fix for root_claim e2e tests
basfroman 031f2ae
remove constants from settings.py
basfroman 8e4ec1a
use constants from the chain + tests
basfroman dd7d2e6
review fixes
basfroman e88a1e2
Merge branch 'staging' into feat/basfroman/add-dynamic-tempo-support
basfroman d81696d
Merge branch 'staging' into feat/basfroman/add-dynamic-tempo-support
basfroman 06bca61
update bittensor-drand deps
basfroman e595a25
extend the range of expected_commit_block bc of fast blocks
basfroman 183cf61
opps
basfroman File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,120 +1,163 @@ | ||
| from dataclasses import dataclass | ||
| from bittensor.utils.balance import fixed_to_float | ||
| import re | ||
| from dataclasses import dataclass, field, fields | ||
| from typing import Any, Optional | ||
|
|
||
| from bittensor.core.chain_data.info_base import InfoBase | ||
| from bittensor.utils import Self | ||
| from bittensor.utils.balance import fixed_to_float | ||
|
|
||
| _FIXED_POINT_TAG = re.compile(r"^[UI]\d+F(\d+)$") | ||
|
|
||
| # Chain field name -> dataclass attribute (v2/v1 use ``max_weights_limit``). | ||
| _FIELD_ALIASES = {"max_weights_limit": "max_weight_limit"} | ||
|
|
||
|
|
||
| @dataclass | ||
| class SubnetHyperparameters(InfoBase): | ||
| """ | ||
| This class represents the hyperparameters for a subnet. | ||
|
|
||
| Attributes: | ||
| rho: The rate of decay of some value. | ||
| kappa: A constant multiplier used in calculations. | ||
| immunity_period: The period during which immunity is active. | ||
| min_allowed_weights: Minimum allowed weights. | ||
| max_weight_limit: Maximum weight limit. | ||
| tempo: The tempo or rate of operation. | ||
| min_difficulty: Minimum difficulty for some operations. | ||
| max_difficulty: Maximum difficulty for some operations. | ||
| weights_version: The version number of the weights used. | ||
| weights_rate_limit: Rate limit for processing weights. | ||
| adjustment_interval: Interval at which adjustments are made. | ||
| activity_cutoff: Activity cutoff threshold. | ||
| registration_allowed: Indicates if registration is allowed. | ||
| target_regs_per_interval: Target number of registrations per interval. | ||
| min_burn: Minimum burn value. | ||
| max_burn: Maximum burn value. | ||
| bonds_moving_avg: Moving average of bonds. | ||
| max_regs_per_block: Maximum number of registrations per block. | ||
| serving_rate_limit: Limit on the rate of service. | ||
| max_validators: Maximum number of validators. | ||
| adjustment_alpha: Alpha value for adjustments. | ||
| difficulty: Difficulty level. | ||
| commit_reveal_period: Interval for commit-reveal weights. | ||
| commit_reveal_weights_enabled: Flag indicating if commit-reveal weights are enabled. | ||
| alpha_high: High value of alpha. | ||
| alpha_low: Low value of alpha. | ||
| liquid_alpha_enabled: Flag indicating if liquid alpha is enabled. | ||
| alpha_sigmoid_steepness: Sigmoid steepness parameter for converting miner-validator alignment into alpha. | ||
| yuma_version: Version of yuma. | ||
| subnet_is_active: Indicates if subnet is active after START CALL. | ||
| transfers_enabled: Flag indicating if transfers are enabled. | ||
| bonds_reset_enabled: Flag indicating if bonds are reset enabled. | ||
| user_liquidity_enabled: Flag indicating if user liquidity is enabled. | ||
| Hyperparameters for a subnet. | ||
|
|
||
| Known fields are explicit typed attributes for IDE support. Values returned | ||
| by the chain under other names are stored in ``hyperparameters`` and are | ||
| accessible via attribute, item, or mapping-style access. | ||
| """ | ||
|
|
||
| rho: int | ||
| kappa: int | ||
| immunity_period: int | ||
| min_allowed_weights: int | ||
| max_weight_limit: float | ||
| tempo: int | ||
| min_difficulty: int | ||
| max_difficulty: int | ||
| weights_version: int | ||
| weights_rate_limit: int | ||
| adjustment_interval: int | ||
| activity_cutoff: int | ||
| registration_allowed: bool | ||
| target_regs_per_interval: int | ||
| min_burn: int | ||
| max_burn: int | ||
| bonds_moving_avg: int | ||
| max_regs_per_block: int | ||
| serving_rate_limit: int | ||
| max_validators: int | ||
| adjustment_alpha: int | ||
| difficulty: int | ||
| commit_reveal_period: int | ||
| commit_reveal_weights_enabled: bool | ||
| alpha_high: int | ||
| alpha_low: int | ||
| liquid_alpha_enabled: bool | ||
| alpha_sigmoid_steepness: float | ||
| yuma_version: int | ||
| subnet_is_active: bool | ||
| transfers_enabled: bool | ||
| bonds_reset_enabled: bool | ||
| user_liquidity_enabled: bool | ||
| rho: Optional[int] = None | ||
| kappa: Optional[int] = None | ||
| immunity_period: Optional[int] = None | ||
| min_allowed_weights: Optional[int] = None | ||
| max_weight_limit: Optional[float] = None | ||
| tempo: Optional[int] = None | ||
| min_difficulty: Optional[int] = None | ||
| max_difficulty: Optional[int] = None | ||
| weights_version: Optional[int] = None | ||
| weights_rate_limit: Optional[int] = None | ||
| adjustment_interval: Optional[int] = None | ||
| activity_cutoff: Optional[int] = None | ||
| registration_allowed: Optional[bool] = None | ||
| target_regs_per_interval: Optional[int] = None | ||
| min_burn: Optional[int] = None | ||
| max_burn: Optional[int] = None | ||
| bonds_moving_avg: Optional[int] = None | ||
| max_regs_per_block: Optional[int] = None | ||
| serving_rate_limit: Optional[int] = None | ||
| max_validators: Optional[int] = None | ||
| adjustment_alpha: Optional[int] = None | ||
| difficulty: Optional[int] = None | ||
| commit_reveal_period: Optional[int] = None | ||
| commit_reveal_weights_enabled: Optional[bool] = None | ||
| alpha_high: Optional[int] = None | ||
| alpha_low: Optional[int] = None | ||
| liquid_alpha_enabled: Optional[bool] = None | ||
| alpha_sigmoid_steepness: Optional[float] = None | ||
| yuma_version: Optional[int] = None | ||
| subnet_is_active: Optional[bool] = None | ||
| transfers_enabled: Optional[bool] = None | ||
| bonds_reset_enabled: Optional[bool] = None | ||
| user_liquidity_enabled: Optional[bool] = None | ||
| activity_cutoff_factor: Optional[int] = None | ||
| hyperparameters: dict[str, Any] = field(default_factory=dict, repr=False) | ||
|
|
||
| @classmethod | ||
| def _from_dict(cls, decoded: dict) -> "SubnetHyperparameters": | ||
| """Returns a SubnetHyperparameters object from decoded chain data.""" | ||
| return SubnetHyperparameters( | ||
| activity_cutoff=decoded["activity_cutoff"], | ||
| adjustment_alpha=decoded["adjustment_alpha"], | ||
| adjustment_interval=decoded["adjustment_interval"], | ||
| alpha_high=decoded["alpha_high"], | ||
| alpha_low=decoded["alpha_low"], | ||
| alpha_sigmoid_steepness=fixed_to_float( | ||
| decoded["alpha_sigmoid_steepness"], frac_bits=32 | ||
| ), | ||
| bonds_moving_avg=decoded["bonds_moving_avg"], | ||
| bonds_reset_enabled=decoded["bonds_reset_enabled"], | ||
| commit_reveal_weights_enabled=decoded["commit_reveal_weights_enabled"], | ||
| commit_reveal_period=decoded["commit_reveal_period"], | ||
| difficulty=decoded["difficulty"], | ||
| immunity_period=decoded["immunity_period"], | ||
| kappa=decoded["kappa"], | ||
| liquid_alpha_enabled=decoded["liquid_alpha_enabled"], | ||
| max_burn=decoded["max_burn"], | ||
| max_difficulty=decoded["max_difficulty"], | ||
| max_regs_per_block=decoded["max_regs_per_block"], | ||
| max_validators=decoded["max_validators"], | ||
| max_weight_limit=decoded["max_weights_limit"], | ||
| min_allowed_weights=decoded["min_allowed_weights"], | ||
| min_burn=decoded["min_burn"], | ||
| min_difficulty=decoded["min_difficulty"], | ||
| registration_allowed=decoded["registration_allowed"], | ||
| rho=decoded["rho"], | ||
| serving_rate_limit=decoded["serving_rate_limit"], | ||
| subnet_is_active=decoded["subnet_is_active"], | ||
| target_regs_per_interval=decoded["target_regs_per_interval"], | ||
| tempo=decoded["tempo"], | ||
| transfers_enabled=decoded["transfers_enabled"], | ||
| user_liquidity_enabled=decoded["user_liquidity_enabled"], | ||
| weights_rate_limit=decoded["weights_rate_limit"], | ||
| weights_version=decoded["weights_version"], | ||
| yuma_version=decoded["yuma_version"], | ||
| @staticmethod | ||
| def _typed_field_names() -> frozenset[str]: | ||
| return frozenset( | ||
| f.name for f in fields(SubnetHyperparameters) if f.name != "hyperparameters" | ||
| ) | ||
|
|
||
| @staticmethod | ||
| def _decode_value(value: Any) -> Any: | ||
| """Decode a single ``{<type_tag>: <payload>}`` hyperparameter value.""" | ||
| if isinstance(value, dict) and set(value.keys()) == {"bits"}: | ||
| # V2 struct encodes fixed-point fields as {"bits": N} without a type tag. All V2 fixed-point fields are | ||
| # I32F32. V3 entries carry the enum variant tag and are handled below. | ||
| return fixed_to_float(value["bits"], frac_bits=32) | ||
| if not isinstance(value, dict) or len(value) != 1: | ||
| return value | ||
| ((type_tag, payload),) = value.items() | ||
| if type_tag == "Bool": | ||
| return bool(payload) | ||
| if match := _FIXED_POINT_TAG.match(type_tag): | ||
| if isinstance(payload, dict) and "bits" in payload: | ||
| payload = payload["bits"] | ||
| return fixed_to_float(payload, frac_bits=int(match.group(1))) | ||
| try: | ||
| return int(payload) | ||
| except (TypeError, ValueError): | ||
| return payload | ||
|
|
||
| @classmethod | ||
| def _fix_decoded(cls, decoded: list | dict | Self) -> Self: | ||
| if isinstance(decoded, SubnetHyperparameters): | ||
| return decoded | ||
|
|
||
| if isinstance(decoded, dict): | ||
| entries = decoded.items() | ||
| else: | ||
| entries = ((record["name"], record["value"]) for record in decoded) | ||
|
|
||
| flat: dict[str, Any] = {} | ||
| for name, value in entries: | ||
| if isinstance(name, (bytes, bytearray)): | ||
| name = name.decode("utf-8", errors="replace") | ||
| if not isinstance(name, str): | ||
| continue | ||
| flat[name] = cls._decode_value(value) | ||
|
|
||
| for chain_name, attr_name in _FIELD_ALIASES.items(): | ||
| if chain_name in flat and attr_name not in flat: | ||
| flat[attr_name] = flat[chain_name] | ||
|
|
||
| typed_names = cls._typed_field_names() | ||
| typed_kwargs = {name: flat[name] for name in typed_names if name in flat} | ||
| spillover = { | ||
| name: value for name, value in flat.items() if name not in typed_names | ||
| } | ||
| return cls(hyperparameters=spillover, **typed_kwargs) | ||
|
|
||
| @classmethod | ||
| def from_any(cls, data: Any) -> Self: | ||
| return cls._fix_decoded(data) | ||
|
|
||
| @classmethod | ||
| def from_dict(cls, decoded: dict) -> Self: | ||
| return cls.from_any(decoded) | ||
|
|
||
| def __getattr__(self, item: str) -> Any: | ||
| try: | ||
| return self.__dict__["hyperparameters"][item] | ||
| except KeyError: | ||
| raise AttributeError( | ||
| f"{type(self).__name__!r} object has no hyperparameter {item!r}" | ||
| ) | ||
|
|
||
| def __getitem__(self, item: str) -> Any: | ||
| if item in self._typed_field_names(): | ||
| return getattr(self, item) | ||
| return self.hyperparameters[item] | ||
|
|
||
| def __iter__(self): | ||
| return self.keys() | ||
|
|
||
| def __contains__(self, item: str) -> bool: | ||
| if item in self._typed_field_names(): | ||
| return getattr(self, item) is not None | ||
| return item in self.hyperparameters | ||
|
|
||
| def get(self, item: str, default: Any = None) -> Any: | ||
| if item in self._typed_field_names(): | ||
| value = getattr(self, item) | ||
| return default if value is None else value | ||
| return self.hyperparameters.get(item, default) | ||
|
|
||
| def items(self): | ||
| for name in sorted(self._typed_field_names()): | ||
| value = getattr(self, name) | ||
| if value is not None: | ||
| yield name, value | ||
| yield from self.hyperparameters.items() | ||
|
|
||
| def keys(self): | ||
| return (name for name, _ in self.items()) | ||
|
|
||
| def values(self): | ||
| return (value for _, value in self.items()) | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.