Skip to content

Commit

Permalink
Merge pull request #2093 from opentensor/sam/debug_childkey
Browse files Browse the repository at this point in the history
Float normalization for child hotkeys
  • Loading branch information
opendansor authored Jul 2, 2024
2 parents 79457b3 + 937f614 commit c34cc8c
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 35 deletions.
19 changes: 11 additions & 8 deletions bittensor/commands/stake/revoke_children.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,12 @@

import argparse
import re
from typing import Union, Tuple
import numpy as np
from numpy.typing import NDArray
from typing import Tuple, List
from rich.prompt import Confirm, Prompt

import bittensor
from .. import defaults, GetChildrenCommand
from ...utils import is_valid_ss58_address

console = bittensor.__console__

Expand Down Expand Up @@ -84,9 +83,13 @@ def _run(cli: "bittensor.cli", subtensor: "bittensor.subtensor"):
# Parse from strings
netuid = cli.config.netuid

children = np.array(
[str(x) for x in re.split(r"[ ,]+", cli.config.children)], dtype=str
)
children = re.split(r"[ ,]+", cli.config.children.strip())

# Validate children SS58 addresses
for child in children:
if not is_valid_ss58_address(child):
console.print(f":cross_mark:[red] Invalid SS58 address: {child}[/red]")
return

success, message = RevokeChildrenCommand.do_revoke_children_multiple(
subtensor=subtensor,
Expand Down Expand Up @@ -152,7 +155,7 @@ def do_revoke_children_multiple(
subtensor: "bittensor.subtensor",
wallet: "bittensor.wallet",
hotkey: str,
children: Union[NDArray[str], list],
children: List[str],
netuid: int,
wait_for_inclusion: bool = True,
wait_for_finalization: bool = False,
Expand All @@ -168,7 +171,7 @@ def do_revoke_children_multiple(
Bittensor wallet object.
hotkey (str):
Parent hotkey.
children (np.ndarray):
children (List[str]):
Children hotkeys.
netuid (int):
Unique identifier of for the subnet.
Expand Down
3 changes: 2 additions & 1 deletion bittensor/commands/stake/set_child.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

import bittensor
from .. import defaults, GetChildrenCommand # type: ignore
from ...utils.formatting import float_to_u64
from ...utils.formatting import float_to_u64, normalize_u64_values

console = bittensor.__console__

Expand Down Expand Up @@ -221,6 +221,7 @@ def do_set_child_singular(
try:
# prepare values for emmit
proportion = float_to_u64(proportion)
proportion = normalize_u64_values([proportion])[0]

call_module = "SubtensorModule"
call_function = "set_child_singular"
Expand Down
34 changes: 21 additions & 13 deletions bittensor/commands/stake/set_children.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,19 @@

import argparse
import re
from typing import Union
from typing import Union, List
from rich.prompt import Confirm
from numpy.typing import NDArray
import numpy as np
from rich.prompt import Prompt
from typing import Tuple
import bittensor
from .. import defaults, GetChildrenCommand # type: ignore
from ...utils.formatting import float_to_u64
from ...utils.formatting import (
float_to_u64,
normalize_u64_values,
is_valid_ss58_address,
)

console = bittensor.__console__

Expand Down Expand Up @@ -93,15 +97,16 @@ def _run(cli: "bittensor.cli", subtensor: "bittensor.subtensor"):
# Parse from strings
netuid = cli.config.netuid

proportions = np.array(
[float(x) for x in re.split(r"[ ,]+", cli.config.proportions)],
dtype=np.float32,
)
children = np.array(
[str(x) for x in re.split(r"[ ,]+", cli.config.children)], dtype=str
)
proportions = [float(x) for x in re.split(r"[ ,]+", cli.config.proportions)]
children = [str(x) for x in re.split(r"[ ,]+", cli.config.children)]

total_proposed = np.sum(proportions) + current_proportions
# Validate children SS58 addresses
for child in children:
if not is_valid_ss58_address(child):
console.print(f":cross_mark:[red] Invalid SS58 address: {child}[/red]")
return

total_proposed = sum(proportions) + current_proportions
if total_proposed > 1:
raise ValueError(
f":cross_mark:[red] The sum of all proportions cannot be greater than 1. Proposed sum of proportions is {total_proposed}[/red]"
Expand Down Expand Up @@ -181,7 +186,7 @@ def do_set_children_multiple(
subtensor: "bittensor.subtensor",
wallet: "bittensor.wallet",
hotkey: str,
children: Union[NDArray[str], list],
children: List[str],
netuid: int,
proportions: Union[NDArray[np.float32], list],
wait_for_inclusion: bool = True,
Expand All @@ -198,7 +203,7 @@ def do_set_children_multiple(
Bittensor wallet object.
hotkey (str):
Parent hotkey.
children (np.ndarray):
children (List[str]):
Children hotkeys.
netuid (int):
Unique identifier of for the subnet.
Expand Down Expand Up @@ -241,11 +246,14 @@ def do_set_children_multiple(
else proportions
)

# Convert each proportion value to u16
# Convert each proportion value to u64
proportions_val = [
float_to_u64(proportion) for proportion in proportions_val
]

# Normalize the u64 values to ensure their sum equals u64::MAX
proportions_val = normalize_u64_values(proportions_val)

children_with_proportions = list(zip(children, proportions_val))

call_module = "SubtensorModule"
Expand Down
73 changes: 62 additions & 11 deletions bittensor/utils/formatting.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import math
from typing import List

import bittensor


def get_human_readable(num, suffix="H"):
Expand Down Expand Up @@ -42,23 +45,71 @@ def u16_to_float(value):
return value / u16_max


def float_to_u64(value):
def float_to_u64(value: float) -> int:
# Ensure the input is within the expected range
if not (0 <= value < 1):
raise ValueError("Input value must be between 0 and 1")

# Calculate the u64 representation
u64_max = 18446744073709551615 # 2^64 - 1
return int(value * u64_max)
# Convert the float to a u64 value
return int(value * (2**64 - 1))


def u64_to_float(value):
# Ensure the input is within the expected range
if not (0 <= value < 18446744073709551615):
u64_max = 2**64 - 1
# Allow for a small margin of error (e.g., 1) to account for potential rounding issues
if not (0 <= value <= u64_max + 1):
raise ValueError(
"Input value must be between 0 and 18446744073709551615 (2^64 - 1)"
f"Input value ({value}) must be between 0 and {u64_max} (2^64 - 1)"
)

# Calculate the float representation
u64_max = 18446744073709551615
return value / u64_max
return min(value / u64_max, 1.0) # Ensure the result is never greater than 1.0


def normalize_u64_values(values: List[int]) -> List[int]:
"""
Normalize a list of u64 values so that their sum equals u64::MAX (2^64 - 1).
"""
if not values:
raise ValueError("Input list cannot be empty")

if any(v < 0 for v in values):
raise ValueError("Input values must be non-negative")

total = sum(values)
if total == 0:
raise ValueError("Sum of input values cannot be zero")

u64_max = 2**64 - 1
normalized = [int((v / total) * u64_max) for v in values]

# Adjust values to ensure sum is exactly u64::MAX
current_sum = sum(normalized)
diff = u64_max - current_sum

for i in range(abs(diff)):
if diff > 0:
normalized[i % len(normalized)] += 1
else:
normalized[i % len(normalized)] = max(
0, normalized[i % len(normalized)] - 1
)

# Final check and adjustment
final_sum = sum(normalized)
if final_sum > u64_max:
normalized[-1] -= final_sum - u64_max

assert (
sum(normalized) == u64_max
), f"Sum of normalized values ({sum(normalized)}) is not equal to u64::MAX ({u64_max})"

return normalized


def is_valid_ss58_address(address: str) -> bool:
"""
Validate that the hotkey address input str is a valid ss58 address.
"""
try:
return bittensor.utils.ss58.is_valid_ss58_address(address)
except:
return False
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,9 @@ def test_set_revoke_child(local_chain, capsys):
)

subtensor = bittensor.subtensor(network="ws://localhost:9945")
assert len(subtensor.get_children_info(netuid=1)) == 1, "failed to set child hotkey"
assert (
len(subtensor.get_children_info(netuid=1)[alice_keypair.ss58_address]) == 1
), "failed to set child hotkey"

output = capsys.readouterr().out
assert "✅ Finalized" in output
Expand Down Expand Up @@ -223,7 +225,7 @@ def test_set_revoke_children(local_chain, capsys):

subtensor = bittensor.subtensor(network="ws://localhost:9945")
assert (
len(subtensor.get_children_info(netuid=1)) == 2
len(subtensor.get_children_info(netuid=1)[alice_keypair.ss58_address]) == 2
), "failed to set children hotkeys"

output = capsys.readouterr().out
Expand Down

0 comments on commit c34cc8c

Please sign in to comment.