Refactor visulization code to python module

This commit is contained in:
Prathamesh Musale 2025-08-12 17:06:22 +05:30
parent b8680f8c07
commit 6b7fca23b1
2 changed files with 385 additions and 325 deletions

View File

@ -17,7 +17,7 @@
},
{
"cell_type": "code",
"execution_count": 19,
"execution_count": 1,
"id": "setup-imports",
"metadata": {},
"outputs": [],
@ -26,12 +26,12 @@
"from lockdrop_calculations import (\n",
" configure_matplotlib, print_constants_summary, calculate_dynamic_allocations,\n",
" calculate_bonus_pools, calculate_final_allocations, generate_test_output,\n",
" print_analysis_tables, create_visualization, PENALTY_RATES, NUM_STARS, NUM_GALAXIES\n",
" print_analysis_tables, create_visualization, PENALTY_RATES, NUM_STARS, NUM_GALAXIES,\n",
" create_experiment_widgets, create_calculate_function, create_scenario_loader, create_export_function\n",
")\n",
"import json\n",
"from decimal import Decimal\n",
"import pandas as pd\n",
"import ipywidgets as widgets\n",
"from IPython.display import display, clear_output\n",
"\n",
"# Configure matplotlib\n",
@ -40,7 +40,7 @@
},
{
"cell_type": "code",
"execution_count": 20,
"execution_count": 2,
"id": "show-constants",
"metadata": {},
"outputs": [
@ -53,24 +53,41 @@
"================================================================================\n",
"\n",
"🔒 LOCKDROP ALLOCATION\n",
" Parameter Value\n",
"Total Supply (1 $Z per Urbit ID) 4,294,967,296\n",
" Lockdrop Allocation % 30.0%\n",
" Lockdrop Allocation ($Z) 1,288,490,188.8\n",
"+----------------------------------+-----------------+\n",
"| Parameter | Value |\n",
"+==================================+=================+\n",
"| Total Supply (1 $Z per Urbit ID) | 4,294,967,296 |\n",
"+----------------------------------+-----------------+\n",
"| Lockdrop Allocation % | 30.0% |\n",
"+----------------------------------+-----------------+\n",
"| Lockdrop Allocation ($Z) | 1,288,490,188.8 |\n",
"+----------------------------------+-----------------+\n",
"\n",
"⭐ URBIT POINTS DISTRIBUTION\n",
"Point Type Count Allocation %\n",
" Galaxies 256 0.39%\n",
" Stars 65,280 99.61%\n",
" Planets 4,294,901,760 0%\n",
"+--------------+---------------+----------------+\n",
"| Point Type | Count | Allocation % |\n",
"+==============+===============+================+\n",
"| Galaxies | 256 | 0.39% |\n",
"+--------------+---------------+----------------+\n",
"| Stars | 65,280 | 99.61% |\n",
"+--------------+---------------+----------------+\n",
"| Planets | 4,294,901,760 | 0% |\n",
"+--------------+---------------+----------------+\n",
"\n",
"⏱️ LOCKDROP PARAMETERS\n",
" Parameter Value\n",
" Block Duration 2 seconds\n",
"Max Point Lock Duration (5 yrs) 157,788,000 seconds\n",
" Total Blocks 78,894,000\n",
" Star Allocation % 0.996093\n",
" Galaxy Allocation % 0.003906\n",
"+---------------------------------+---------------------+\n",
"| Parameter | Value |\n",
"+=================================+=====================+\n",
"| Block Duration | 2 seconds |\n",
"+---------------------------------+---------------------+\n",
"| Max Point Lock Duration (5 yrs) | 157,788,000 seconds |\n",
"+---------------------------------+---------------------+\n",
"| Total Blocks | 78,894,000 |\n",
"+---------------------------------+---------------------+\n",
"| Star Allocation % | 99.609375% |\n",
"+---------------------------------+---------------------+\n",
"| Galaxy Allocation % | 0.390625% |\n",
"+---------------------------------+---------------------+\n",
"\n",
"================================================================================\n"
]
@ -93,7 +110,7 @@
},
{
"cell_type": "code",
"execution_count": 21,
"execution_count": 3,
"id": "input-parameters",
"metadata": {},
"outputs": [
@ -101,6 +118,7 @@
"name": "stdout",
"output_type": "stream",
"text": [
"==================================================\n",
"🎯 PARTICIPATION INPUT CONTROLS\n",
"==================================================\n",
"\n",
@ -110,7 +128,7 @@
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "79244d07df4e4ee6923dda69c1febc2e",
"model_id": "75120f519bd64aeb88163fdfb2f9aaed",
"version_major": 2,
"version_minor": 0
},
@ -132,7 +150,7 @@
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "4071b25411c44a67b26f5924a2507c0c",
"model_id": "83e348a914854ce0a4b3e75c58ecc94e",
"version_major": 2,
"version_minor": 0
},
@ -146,7 +164,7 @@
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "1c785fe3828d4d5eb62a9f35755f29f8",
"model_id": "d851d648f30247e2b98b4bd57a101562",
"version_major": 2,
"version_minor": 0
},
@ -157,18 +175,10 @@
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"🌌 CALCULATIONS\n"
]
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "bc3937e163a34ff58f215f94a36aee3e",
"model_id": "39b0b853b4bb4971b0ebbaa2fcd2e8e0",
"version_major": 2,
"version_minor": 0
},
@ -181,159 +191,38 @@
}
],
"source": [
"# Define reasonable default values\n",
"DEFAULT_PARTICIPANTS = {\n",
" 'stars_1_years': 8000,\n",
" 'stars_2_years': 8000,\n",
" 'stars_3_years': 8000,\n",
" 'stars_4_years': 8000,\n",
" 'stars_5_years': 8000,\n",
" 'galaxies_1_years': 40,\n",
" 'galaxies_2_years': 40,\n",
" 'galaxies_3_years': 40,\n",
" 'galaxies_4_years': 40,\n",
" 'galaxies_5_years': 40\n",
"}\n",
"# Create experiment widgets using refactored function\n",
"widget_dict = create_experiment_widgets()\n",
"\n",
"# Create input widgets\n",
"# Display widgets\n",
"print(\"🎯 PARTICIPATION INPUT CONTROLS\")\n",
"print(\"=\"*50)\n",
"\n",
"# Star participation widgets\n",
"print(\"\\n⭐ STAR PARTICIPATION\")\n",
"stars_1_years = widgets.IntText(value=DEFAULT_PARTICIPANTS['stars_1_years'], description='1 Year:', style={'description_width': '80px'})\n",
"stars_2_years = widgets.IntText(value=DEFAULT_PARTICIPANTS['stars_2_years'], description='2 Years:', style={'description_width': '80px'})\n",
"stars_3_years = widgets.IntText(value=DEFAULT_PARTICIPANTS['stars_3_years'], description='3 Years:', style={'description_width': '80px'})\n",
"stars_4_years = widgets.IntText(value=DEFAULT_PARTICIPANTS['stars_4_years'], description='4 Years:', style={'description_width': '80px'})\n",
"stars_5_years = widgets.IntText(value=DEFAULT_PARTICIPANTS['stars_5_years'], description='5 Years:', style={'description_width': '80px'})\n",
"\n",
"star_controls = widgets.VBox([stars_1_years, stars_2_years, stars_3_years, stars_4_years, stars_5_years])\n",
"display(star_controls)\n",
"display(widget_dict['star_controls'])\n",
"\n",
"print(\"\\n🌌 GALAXY PARTICIPATION\")\n",
"galaxies_1_years = widgets.IntText(value=DEFAULT_PARTICIPANTS['galaxies_1_years'], description='1 Year:', style={'description_width': '80px'})\n",
"galaxies_2_years = widgets.IntText(value=DEFAULT_PARTICIPANTS['galaxies_2_years'], description='2 Years:', style={'description_width': '80px'})\n",
"galaxies_3_years = widgets.IntText(value=DEFAULT_PARTICIPANTS['galaxies_3_years'], description='3 Years:', style={'description_width': '80px'})\n",
"galaxies_4_years = widgets.IntText(value=DEFAULT_PARTICIPANTS['galaxies_4_years'], description='4 Years:', style={'description_width': '80px'})\n",
"galaxies_5_years = widgets.IntText(value=DEFAULT_PARTICIPANTS['galaxies_5_years'], description='5 Years:', style={'description_width': '80px'})\n",
"\n",
"galaxy_controls = widgets.VBox([galaxies_1_years, galaxies_2_years, galaxies_3_years, galaxies_4_years, galaxies_5_years])\n",
"display(galaxy_controls)\n",
"display(widget_dict['galaxy_controls'])\n",
"\n",
"# Calculate button\n",
"calculate_button = widgets.Button(description='🔄 Calculate Allocations', button_style='success', layout={'width': '200px', 'margin': '20px 0'})\n",
"display(calculate_button)\n",
"display(widget_dict['calculate_button'])\n",
"\n",
"# Output area\n",
"print(\"\\n🌌 CALCULATIONS\")\n",
"output_area = widgets.Output()\n",
"display(output_area)"
"display(widget_dict['output_area'])"
]
},
{
"cell_type": "code",
"execution_count": 22,
"execution_count": 4,
"id": "calculation-function",
"metadata": {},
"outputs": [],
"source": [
"def calculate_and_display(button=None):\n",
" \"\"\"Calculate allocations and display results based on input parameters.\"\"\"\n",
" with output_area:\n",
" clear_output(wait=True)\n",
"\n",
" # Get current values from widgets\n",
" participation_counts = {\n",
" 'stars_1_years': stars_1_years.value,\n",
" 'stars_2_years': stars_2_years.value,\n",
" 'stars_3_years': stars_3_years.value,\n",
" 'stars_4_years': stars_4_years.value,\n",
" 'stars_5_years': stars_5_years.value,\n",
" 'galaxies_1_years': galaxies_1_years.value,\n",
" 'galaxies_2_years': galaxies_2_years.value,\n",
" 'galaxies_3_years': galaxies_3_years.value,\n",
" 'galaxies_4_years': galaxies_4_years.value,\n",
" 'galaxies_5_years': galaxies_5_years.value\n",
" }\n",
"\n",
" # Validate inputs\n",
" total_stars = sum(participation_counts[key] for key in participation_counts if 'stars' in key)\n",
" total_galaxies = sum(participation_counts[key] for key in participation_counts if 'galaxies' in key)\n",
"\n",
" if total_stars > NUM_STARS:\n",
" print(f\"⚠️ ERROR: Total stars ({total_stars:,}) exceeds maximum available ({NUM_STARS:,})\")\n",
" return\n",
"\n",
" if total_galaxies > NUM_GALAXIES:\n",
" print(f\"⚠️ ERROR: Total galaxies ({total_galaxies:,}) exceeds maximum available ({NUM_GALAXIES:,})\")\n",
" return\n",
"\n",
" if total_stars == 0 and total_galaxies == 0:\n",
" print(\"⚠️ ERROR: Must have at least some participants\")\n",
" return\n",
"\n",
" try:\n",
" # Perform calculations\n",
" print(\"🔄 Calculating allocations...\\n\")\n",
"\n",
" allocation_data = calculate_dynamic_allocations(participation_counts)\n",
" bonus_data = calculate_bonus_pools(allocation_data)\n",
" final_data = calculate_final_allocations(allocation_data, bonus_data)\n",
"\n",
" # Display results\n",
" print_analysis_tables(allocation_data, bonus_data, final_data)\n",
"\n",
" # Show penalty rates for reference\n",
" print(\"\\n📋 PENALTY SYSTEM REFERENCE\")\n",
" penalty_df = pd.DataFrame({\n",
" 'Lock Period': ['5 Years', '4 Years', '3 Years', '2 Years', '1 Year'],\n",
" 'Penalty Rate': [f\"{PENALTY_RATES[year]:.1%}\" for year in [5, 4, 3, 2, 1]],\n",
" 'Description': ['No penalty + Bonus', '20% penalty', '40% penalty', '60% penalty', '80% penalty']\n",
" })\n",
" print(penalty_df.to_string(index=False))\n",
"\n",
" # Create visualization\n",
" print(\"\\n📊 Generating visualization...\")\n",
" create_visualization(allocation_data, final_data)\n",
"\n",
" # Calculate participation insights\n",
" star_participation_rate = Decimal(total_stars) / NUM_STARS\n",
" galaxy_participation_rate = Decimal(total_galaxies) / NUM_GALAXIES\n",
"\n",
" print(\"\\n\" + \"=\"*80)\n",
" print(\"🎯 EXPERIMENT INSIGHTS\")\n",
" print(\"=\"*80)\n",
"\n",
" insights_df = pd.DataFrame({\n",
" 'Metric': [\n",
" 'Total Participants',\n",
" 'Star Participation Rate',\n",
" 'Galaxy Participation Rate',\n",
" '5-Year Star Bonus',\n",
" '5-Year Galaxy Bonus',\n",
" 'Highest Individual Allocation',\n",
" 'Lowest Individual Allocation'\n",
" ],\n",
" 'Value': [\n",
" f\"{total_stars + total_galaxies:,}\",\n",
" f\"{star_participation_rate:.1%}\",\n",
" f\"{galaxy_participation_rate:.1%}\",\n",
" f\"{float(bonus_data['bonus_per_star_5_years']):,.2f} $Z\" if participation_counts['stars_5_years'] > 0 else \"N/A (no 5Y stars)\",\n",
" f\"{float(bonus_data['bonus_per_galaxy_5_years']):,.2f} $Z\" if participation_counts['galaxies_5_years'] > 0 else \"N/A (no 5Y galaxies)\",\n",
" f\"{max(float(final_data['final_star_allocations'][5]), float(final_data['final_galaxy_allocations'][5])):,.2f} $Z\",\n",
" f\"{min(float(final_data['final_star_allocations'][1]), float(final_data['final_galaxy_allocations'][1])):,.2f} $Z\"\n",
" ]\n",
" })\n",
" print(insights_df.to_string(index=False))\n",
" print(\"\\n\" + \"=\"*80)\n",
"\n",
" except Exception as e:\n",
" print(f\"❌ Error during calculation: {str(e)}\")\n",
" import traceback\n",
" traceback.print_exc()\n",
"# Create calculation function using refactored approach\n",
"calculate_and_display = create_calculate_function(widget_dict)\n",
"\n",
"# Connect button to calculation function\n",
"calculate_button.on_click(calculate_and_display)"
"widget_dict['calculate_button'].on_click(calculate_and_display)"
]
},
{
@ -348,7 +237,7 @@
},
{
"cell_type": "code",
"execution_count": 23,
"execution_count": 5,
"id": "preset-scenarios",
"metadata": {},
"outputs": [
@ -376,75 +265,23 @@
}
],
"source": [
"# Define preset scenarios\n",
"SCENARIOS = {\n",
" 'balanced': {\n",
" 'description': 'Balanced distribution across all lock periods',\n",
" 'params': {\n",
" 'stars_1_years': 8000, 'stars_2_years': 8000, 'stars_3_years': 8000, 'stars_4_years': 8000, 'stars_5_years': 8000,\n",
" 'galaxies_1_years': 40, 'galaxies_2_years': 40, 'galaxies_3_years': 40, 'galaxies_4_years': 40, 'galaxies_5_years': 40\n",
" }\n",
" },\n",
" 'five_year_focused': {\n",
" 'description': 'Most participants choose 5-year lock (maximum bonus scenario)',\n",
" 'params': {\n",
" 'stars_1_years': 2000, 'stars_2_years': 2000, 'stars_3_years': 3000, 'stars_4_years': 5000, 'stars_5_years': 28000,\n",
" 'galaxies_1_years': 10, 'galaxies_2_years': 10, 'galaxies_3_years': 20, 'galaxies_4_years': 30, 'galaxies_5_years': 130\n",
" }\n",
" },\n",
" 'short_term_focused': {\n",
" 'description': 'Most participants choose shorter locks (high penalty scenario)',\n",
" 'params': {\n",
" 'stars_1_years': 20000, 'stars_2_years': 15000, 'stars_3_years': 4000, 'stars_4_years': 800, 'stars_5_years': 200,\n",
" 'galaxies_1_years': 100, 'galaxies_2_years': 80, 'galaxies_3_years': 15, 'galaxies_4_years': 3, 'galaxies_5_years': 2\n",
" }\n",
" },\n",
" 'polarized': {\n",
" 'description': 'Split between 1-year and 5-year locks (maximum bonus per 5Y participant)',\n",
" 'params': {\n",
" 'stars_1_years': 25000, 'stars_2_years': 0, 'stars_3_years': 0, 'stars_4_years': 0, 'stars_5_years': 15000,\n",
" 'galaxies_1_years': 150, 'galaxies_2_years': 0, 'galaxies_3_years': 0, 'galaxies_4_years': 0, 'galaxies_5_years': 50\n",
" }\n",
" },\n",
" 'low_participation': {\n",
" 'description': 'Low overall participation scenario',\n",
" 'params': {\n",
" 'stars_1_years': 1000, 'stars_2_years': 800, 'stars_3_years': 600, 'stars_4_years': 400, 'stars_5_years': 200,\n",
" 'galaxies_1_years': 8, 'galaxies_2_years': 6, 'galaxies_3_years': 4, 'galaxies_4_years': 2, 'galaxies_5_years': 5\n",
" }\n",
" }\n",
"}\n",
"\n",
"def load_scenario(scenario_name):\n",
" \"\"\"Load a preset scenario into the input widgets.\"\"\"\n",
" if scenario_name not in SCENARIOS:\n",
" print(f\"❌ Unknown scenario: {scenario_name}\")\n",
" return\n",
"\n",
" scenario = SCENARIOS[scenario_name]\n",
" params = scenario['params']\n",
"\n",
" # Update widget values\n",
" stars_1_years.value = params['stars_1_years']\n",
" stars_2_years.value = params['stars_2_years']\n",
" stars_3_years.value = params['stars_3_years']\n",
" stars_4_years.value = params['stars_4_years']\n",
" stars_5_years.value = params['stars_5_years']\n",
" galaxies_1_years.value = params['galaxies_1_years']\n",
" galaxies_2_years.value = params['galaxies_2_years']\n",
" galaxies_3_years.value = params['galaxies_3_years']\n",
" galaxies_4_years.value = params['galaxies_4_years']\n",
" galaxies_5_years.value = params['galaxies_5_years']\n",
"\n",
" print(f\"✅ Loaded scenario: {scenario_name}\")\n",
" print(f\"📝 Description: {scenario['description']}\")\n",
" print(\"\\n🔄 Click 'Calculate Allocations' button above to see results!\")\n",
"# Create scenario loader using refactored function\n",
"load_scenario = create_scenario_loader(widget_dict)\n",
"\n",
"# Display available scenarios\n",
"print(\"📋 AVAILABLE PRESET SCENARIOS:\")\n",
"print(\"=\"*50)\n",
"for name, scenario in SCENARIOS.items():\n",
" print(f\"\\n• {name}: {scenario['description']}\")\n",
"\n",
"scenarios = {\n",
" 'balanced': 'Balanced distribution across all lock periods',\n",
" 'five_year_focused': 'Most participants choose 5-year lock (maximum bonus scenario)',\n",
" 'short_term_focused': 'Most participants choose shorter locks (high penalty scenario)',\n",
" 'polarized': 'Split between 1-year and 5-year locks (maximum bonus per 5Y participant)',\n",
" 'low_participation': 'Low overall participation scenario'\n",
"}\n",
"\n",
"for name, description in scenarios.items():\n",
" print(f\"\\n• {name}: {description}\")\n",
"\n",
"print(\"\\n🎯 To load a scenario, call: load_scenario('scenario_name')\")\n",
"print(\"\\nExample: load_scenario('five_year_focused')\")"
@ -452,24 +289,13 @@
},
{
"cell_type": "code",
"execution_count": 24,
"execution_count": 6,
"id": "load-scenario-example",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"✅ Loaded scenario: five_year_focused\n",
"📝 Description: Most participants choose 5-year lock (maximum bonus scenario)\n",
"\n",
"🔄 Click 'Calculate Allocations' button above to see results!\n"
]
}
],
"outputs": [],
"source": [
"# Load the balanced scenario as default\n",
"load_scenario('five_year_focused')"
"# load_scenario('short_term_focused')"
]
},
{
@ -479,12 +305,12 @@
"source": [
"## 💾 Export Results\n",
"\n",
"Export your experimental results for use with the test suite:"
"Export your experimental results."
]
},
{
"cell_type": "code",
"execution_count": 25,
"execution_count": 7,
"id": "export-results",
"metadata": {},
"outputs": [
@ -501,50 +327,8 @@
}
],
"source": [
"def export_current_scenario(filename='lockdrop_allocations_experiment.json'):\n",
" \"\"\"Export current scenario results to JSON file.\"\"\"\n",
" # Get current values from widgets\n",
" participation_counts = {\n",
" 'stars_1_years': stars_1_years.value,\n",
" 'stars_2_years': stars_2_years.value,\n",
" 'stars_3_years': stars_3_years.value,\n",
" 'stars_4_years': stars_4_years.value,\n",
" 'stars_5_years': stars_5_years.value,\n",
" 'galaxies_1_years': galaxies_1_years.value,\n",
" 'galaxies_2_years': galaxies_2_years.value,\n",
" 'galaxies_3_years': galaxies_3_years.value,\n",
" 'galaxies_4_years': galaxies_4_years.value,\n",
" 'galaxies_5_years': galaxies_5_years.value\n",
" }\n",
"\n",
" try:\n",
" # Calculate allocations\n",
" allocation_data = calculate_dynamic_allocations(participation_counts)\n",
" bonus_data = calculate_bonus_pools(allocation_data)\n",
" final_data = calculate_final_allocations(allocation_data, bonus_data)\n",
"\n",
" # Generate test output\n",
" test_output = generate_test_output(final_data)\n",
"\n",
" # Add metadata\n",
" export_data = {\n",
" 'metadata': {\n",
" 'source': 'lockdrop-experiment.ipynb',\n",
" 'scenario': 'custom',\n",
" 'participation_counts': participation_counts\n",
" },\n",
" 'allocations': test_output\n",
" }\n",
"\n",
" # Save to file\n",
" with open(filename, 'w') as f:\n",
" json.dump(export_data, f, indent=2)\n",
"\n",
" print(f\"✅ Results exported to: {filename}\")\n",
" print(f\"📊 Total participants: {sum(participation_counts.values()):,}\")\n",
"\n",
" except Exception as e:\n",
" print(f\"❌ Export failed: {str(e)}\")\n",
"# Create export function using refactored approach\n",
"export_current_scenario = create_export_function(widget_dict)\n",
"\n",
"# Usage example\n",
"print(\"💾 Export your current scenario results:\")\n",
@ -554,39 +338,20 @@
]
},
{
"cell_type": "markdown",
"id": "tips-section",
"cell_type": "code",
"execution_count": 8,
"id": "3c93c9c2-9cec-4d2b-a2db-065602a3871c",
"metadata": {},
"outputs": [],
"source": [
"## 💡 Experimentation Tips\n",
"\n",
"### Interesting Things to Try:\n",
"\n",
"1. **Bonus Pool Impact**: Set most participants to 1-2 years and a few to 5 years to see massive bonuses\n",
"2. **Participation Rate Effects**: Try very low participation vs. high participation scenarios\n",
"3. **Star vs Galaxy Balance**: Experiment with different star/galaxy ratios\n",
"4. **Edge Cases**: What happens with only 5-year participants? Only 1-year?\n",
"5. **Realistic Scenarios**: Model expected real-world behavior patterns\n",
"\n",
"### Key Metrics to Watch:\n",
"\n",
"- **5-Year Bonus Multiplier**: How much extra do 5-year participants get?\n",
"- **Participation Rates**: What percentage of each point type participates?\n",
"- **Penalty Pool Size**: How much bonus is generated from penalties?\n",
"- **Allocation Spread**: Difference between highest and lowest allocations\n",
"\n",
"### Understanding the Math:\n",
"\n",
"- Penalties from shorter lock periods create bonus pools\n",
"- All bonus pools go to 5-year participants\n",
"- More penalty = more bonus for 5-year participants\n",
"- Lower participation = higher individual allocations"
"# Export allocation results from the experiment\n",
"# export_current_scenario(\"lockdrop_allocations_experiment.json\")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "venv",
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},

View File

@ -509,8 +509,8 @@ def print_final_allocations_and_verification(allocation_data, final_data):
star_final_df = pd.DataFrame({
'Lock Period': ['5 Years', '4 Years', '3 Years', '2 Years', '1 Year'],
'Penalty': [f"{PENALTY_RATES[year]:.1%}" for year in [5, 4, 3, 2, 1]],
'Final Allocation ($Z)': [f"{final_star_allocations[year]:.12f}" for year in [5, 4, 3, 2, 1]],
'Z per Block': [f"{star_z_per_block[year]:.12f}" for year in [5, 4, 3, 2, 1]],
'Final Allocation ($Z)': [f"{final_star_allocations[year]:.8f}" for year in [5, 4, 3, 2, 1]],
'Z per Block': [f"{star_z_per_block[year]:.8f}" for year in [5, 4, 3, 2, 1]],
'Participants': [f"{stars_counts[year]:,}" for year in [5, 4, 3, 2, 1]]
})
print_table_with_borders(star_final_df, "⭐ FINAL STAR ALLOCATIONS")
@ -519,8 +519,8 @@ def print_final_allocations_and_verification(allocation_data, final_data):
galaxy_final_df = pd.DataFrame({
'Lock Period': ['5 Years', '4 Years', '3 Years', '2 Years', '1 Year'],
'Penalty': [f"{PENALTY_RATES[year]:.1%}" for year in [5, 4, 3, 2, 1]],
'Final Allocation ($Z)': [f"{final_galaxy_allocations[year]:.12f}" for year in [5, 4, 3, 2, 1]],
'Z per Block': [f"{galaxy_z_per_block[year]:.12f}" for year in [5, 4, 3, 2, 1]],
'Final Allocation ($Z)': [f"{final_galaxy_allocations[year]:.8f}" for year in [5, 4, 3, 2, 1]],
'Z per Block': [f"{galaxy_z_per_block[year]:.8f}" for year in [5, 4, 3, 2, 1]],
'Participants': [f"{galaxies_counts[year]:,}" for year in [5, 4, 3, 2, 1]]
})
print_table_with_borders(galaxy_final_df, "🌌 FINAL GALAXY ALLOCATIONS")
@ -534,14 +534,15 @@ def print_final_allocations_and_verification(allocation_data, final_data):
verification_df = pd.DataFrame({
'Category': ['Star Allocations', 'Galaxy Allocations', 'Combined'],
'Calculated Total': [f"{total_stars_allocation:.12f} $Z", f"{total_galaxies_allocation:.12f} $Z",
f"{total_stars_allocation + total_galaxies_allocation:.12f} $Z"],
'Expected Total': [f"{lockdrop_allocation_stars:.12f} $Z", f"{lockdrop_allocation_galaxies:.12f} $Z",
f"{LOCKDROP_ALLOCATION:.12f} $Z"],
'Rounding Error': [f"{final_rounding_error_stars:.12f} $Z", f"{final_rounding_error_galaxies:.12f} $Z",
f"{final_rounding_error:.12f} $Z"]
'Calculated Total': [f"{total_stars_allocation:.8f} $Z", f"{total_galaxies_allocation:.8f} $Z",
f"{total_stars_allocation + total_galaxies_allocation:.8f} $Z"],
'Expected Total': [f"{lockdrop_allocation_stars:.8f} $Z", f"{lockdrop_allocation_galaxies:.8f} $Z",
f"{LOCKDROP_ALLOCATION:.8f} $Z"],
'Rounding Error': [f"{final_rounding_error_stars:.8f} $Z", f"{final_rounding_error_galaxies:.8f} $Z",
f"{final_rounding_error:.8f} $Z"]
})
print_table_with_borders(verification_df, "🔍 ALLOCATION VERIFICATION")
print("\n📝 NOTE: Final rounding errors go to Zenith Foundation")
print("\n" + "="*80)
@ -685,3 +686,297 @@ def load_watcher_events_data(generated_dir='generated'):
except Exception as e:
print(f"⚠️ Error loading watcher events: {e}")
return None, False
# Experimental notebook widget and interaction functions
def create_experiment_widgets():
"""Create input widgets for the experimental notebook."""
try:
import ipywidgets as widgets
except ImportError:
raise ImportError("ipywidgets is required for the experimental interface. Install with: pip install ipywidgets")
# Define reasonable default values
default_participants = {
'stars_1_years': 8000,
'stars_2_years': 8000,
'stars_3_years': 8000,
'stars_4_years': 8000,
'stars_5_years': 8000,
'galaxies_1_years': 40,
'galaxies_2_years': 40,
'galaxies_3_years': 40,
'galaxies_4_years': 40,
'galaxies_5_years': 40
}
# Create input widgets
print("="*50)
# Star participation widgets
stars_1_years = widgets.IntText(value=default_participants['stars_1_years'], description='1 Year:', style={'description_width': '80px'})
stars_2_years = widgets.IntText(value=default_participants['stars_2_years'], description='2 Years:', style={'description_width': '80px'})
stars_3_years = widgets.IntText(value=default_participants['stars_3_years'], description='3 Years:', style={'description_width': '80px'})
stars_4_years = widgets.IntText(value=default_participants['stars_4_years'], description='4 Years:', style={'description_width': '80px'})
stars_5_years = widgets.IntText(value=default_participants['stars_5_years'], description='5 Years:', style={'description_width': '80px'})
star_controls = widgets.VBox([stars_1_years, stars_2_years, stars_3_years, stars_4_years, stars_5_years])
galaxies_1_years = widgets.IntText(value=default_participants['galaxies_1_years'], description='1 Year:', style={'description_width': '80px'})
galaxies_2_years = widgets.IntText(value=default_participants['galaxies_2_years'], description='2 Years:', style={'description_width': '80px'})
galaxies_3_years = widgets.IntText(value=default_participants['galaxies_3_years'], description='3 Years:', style={'description_width': '80px'})
galaxies_4_years = widgets.IntText(value=default_participants['galaxies_4_years'], description='4 Years:', style={'description_width': '80px'})
galaxies_5_years = widgets.IntText(value=default_participants['galaxies_5_years'], description='5 Years:', style={'description_width': '80px'})
galaxy_controls = widgets.VBox([galaxies_1_years, galaxies_2_years, galaxies_3_years, galaxies_4_years, galaxies_5_years])
# Calculate button
calculate_button = widgets.Button(description='🔄 Calculate Allocations', button_style='success', layout={'width': '200px', 'margin': '20px 0'})
# Output area
output_area = widgets.Output()
# Return all widgets as a dictionary
return {
'star_controls': star_controls,
'galaxy_controls': galaxy_controls,
'calculate_button': calculate_button,
'output_area': output_area,
'widgets': {
'stars_1_years': stars_1_years,
'stars_2_years': stars_2_years,
'stars_3_years': stars_3_years,
'stars_4_years': stars_4_years,
'stars_5_years': stars_5_years,
'galaxies_1_years': galaxies_1_years,
'galaxies_2_years': galaxies_2_years,
'galaxies_3_years': galaxies_3_years,
'galaxies_4_years': galaxies_4_years,
'galaxies_5_years': galaxies_5_years
}
}
def create_calculate_function(widget_dict):
"""Create the calculation and display function for the experimental notebook."""
def calculate_and_display(button=None):
"""Calculate allocations and display results based on input parameters."""
try:
from IPython.display import clear_output
except ImportError:
raise ImportError("IPython is required for the experimental interface")
widgets_map = widget_dict['widgets']
output_area = widget_dict['output_area']
with output_area:
clear_output(wait=True)
# Get current values from widgets
participation_counts = {
'stars_1_years': widgets_map['stars_1_years'].value,
'stars_2_years': widgets_map['stars_2_years'].value,
'stars_3_years': widgets_map['stars_3_years'].value,
'stars_4_years': widgets_map['stars_4_years'].value,
'stars_5_years': widgets_map['stars_5_years'].value,
'galaxies_1_years': widgets_map['galaxies_1_years'].value,
'galaxies_2_years': widgets_map['galaxies_2_years'].value,
'galaxies_3_years': widgets_map['galaxies_3_years'].value,
'galaxies_4_years': widgets_map['galaxies_4_years'].value,
'galaxies_5_years': widgets_map['galaxies_5_years'].value
}
# Validate inputs
total_stars = sum(participation_counts[key] for key in participation_counts if 'stars' in key)
total_galaxies = sum(participation_counts[key] for key in participation_counts if 'galaxies' in key)
if total_stars > NUM_STARS:
print(f"⚠️ ERROR: Total stars ({total_stars:,}) exceeds maximum available ({NUM_STARS:,})")
return
if total_galaxies > NUM_GALAXIES:
print(f"⚠️ ERROR: Total galaxies ({total_galaxies:,}) exceeds maximum available ({NUM_GALAXIES:,})")
return
if total_stars == 0 and total_galaxies == 0:
print("⚠️ ERROR: Must have at least some participants")
return
try:
# Perform calculations
print("🔄 Calculating allocations...\n")
allocation_data = calculate_dynamic_allocations(participation_counts)
bonus_data = calculate_bonus_pools(allocation_data)
final_data = calculate_final_allocations(allocation_data, bonus_data)
# Display results
print_analysis_tables(allocation_data, bonus_data, final_data)
# Create visualization
print("\n📊 Generating visualization...")
create_visualization(allocation_data, final_data)
# Calculate participation insights
star_participation_rate = Decimal(total_stars) / NUM_STARS
galaxy_participation_rate = Decimal(total_galaxies) / NUM_GALAXIES
print("\n" + "="*80)
print("🎯 EXPERIMENT INSIGHTS")
print("=" * 80)
insights_df = pd.DataFrame({
'Metric': [
'Total Participants',
'Star Participation Rate',
'Galaxy Participation Rate',
'5-Year Star Bonus',
'5-Year Galaxy Bonus',
'Highest Individual Allocation',
'Lowest Individual Allocation'
],
'Value': [
f"{total_stars + total_galaxies:,}",
f"{star_participation_rate:.1%}",
f"{galaxy_participation_rate:.1%}",
f"{float(bonus_data['bonus_per_star_5_years']):,.2f} $Z" if participation_counts['stars_5_years'] > 0 else "N/A (no 5Y stars)",
f"{float(bonus_data['bonus_per_galaxy_5_years']):,.2f} $Z" if participation_counts['galaxies_5_years'] > 0 else "N/A (no 5Y galaxies)",
f"{max(float(final_data['final_star_allocations'][5]), float(final_data['final_galaxy_allocations'][5])):,.2f} $Z",
f"{min(float(final_data['final_star_allocations'][1]), float(final_data['final_galaxy_allocations'][1])):,.2f} $Z"
]
})
print_table_with_borders(insights_df, "🎯 EXPERIMENT INSIGHTS")
print("\n" + "="*80)
except Exception as e:
print(f"❌ Error during calculation: {str(e)}")
import traceback
traceback.print_exc()
return calculate_and_display
def get_preset_scenarios():
"""Get preset scenario definitions for experimentation."""
return {
'balanced': {
'description': 'Balanced distribution across all lock periods',
'params': {
'stars_1_years': 8000, 'stars_2_years': 8000, 'stars_3_years': 8000, 'stars_4_years': 8000, 'stars_5_years': 8000,
'galaxies_1_years': 40, 'galaxies_2_years': 40, 'galaxies_3_years': 40, 'galaxies_4_years': 40, 'galaxies_5_years': 40
}
},
'five_year_focused': {
'description': 'Most participants choose 5-year lock (maximum bonus scenario)',
'params': {
'stars_1_years': 2000, 'stars_2_years': 2000, 'stars_3_years': 3000, 'stars_4_years': 5000, 'stars_5_years': 28000,
'galaxies_1_years': 10, 'galaxies_2_years': 10, 'galaxies_3_years': 20, 'galaxies_4_years': 30, 'galaxies_5_years': 130
}
},
'short_term_focused': {
'description': 'Most participants choose shorter locks (high penalty scenario)',
'params': {
'stars_1_years': 20000, 'stars_2_years': 15000, 'stars_3_years': 4000, 'stars_4_years': 800, 'stars_5_years': 200,
'galaxies_1_years': 100, 'galaxies_2_years': 80, 'galaxies_3_years': 15, 'galaxies_4_years': 3, 'galaxies_5_years': 2
}
},
'polarized': {
'description': 'Split between 1-year and 5-year locks (maximum bonus per 5Y participant)',
'params': {
'stars_1_years': 25000, 'stars_2_years': 0, 'stars_3_years': 0, 'stars_4_years': 0, 'stars_5_years': 15000,
'galaxies_1_years': 150, 'galaxies_2_years': 0, 'galaxies_3_years': 0, 'galaxies_4_years': 0, 'galaxies_5_years': 50
}
},
'low_participation': {
'description': 'Low overall participation scenario',
'params': {
'stars_1_years': 1000, 'stars_2_years': 800, 'stars_3_years': 600, 'stars_4_years': 400, 'stars_5_years': 200,
'galaxies_1_years': 8, 'galaxies_2_years': 6, 'galaxies_3_years': 4, 'galaxies_4_years': 2, 'galaxies_5_years': 5
}
}
}
def create_scenario_loader(widget_dict):
"""Create scenario loading function for the experimental notebook."""
def load_scenario(scenario_name):
"""Load a preset scenario into the input widgets."""
scenarios = get_preset_scenarios()
if scenario_name not in scenarios:
print(f"❌ Unknown scenario: {scenario_name}")
return
scenario = scenarios[scenario_name]
params = scenario['params']
widgets_map = widget_dict['widgets']
# Update widget values
widgets_map['stars_1_years'].value = params['stars_1_years']
widgets_map['stars_2_years'].value = params['stars_2_years']
widgets_map['stars_3_years'].value = params['stars_3_years']
widgets_map['stars_4_years'].value = params['stars_4_years']
widgets_map['stars_5_years'].value = params['stars_5_years']
widgets_map['galaxies_1_years'].value = params['galaxies_1_years']
widgets_map['galaxies_2_years'].value = params['galaxies_2_years']
widgets_map['galaxies_3_years'].value = params['galaxies_3_years']
widgets_map['galaxies_4_years'].value = params['galaxies_4_years']
widgets_map['galaxies_5_years'].value = params['galaxies_5_years']
print(f"✅ Loaded scenario: {scenario_name}")
print(f"📝 Description: {scenario['description']}")
print("\n🔄 Click 'Calculate Allocations' button above to see results!")
return load_scenario
def create_export_function(widget_dict):
"""Create export function for the experimental notebook."""
def export_current_scenario(filename='lockdrop_allocations_experiment.json'):
"""Export current scenario results to JSON file."""
widgets_map = widget_dict['widgets']
# Get current values from widgets
participation_counts = {
'stars_1_years': widgets_map['stars_1_years'].value,
'stars_2_years': widgets_map['stars_2_years'].value,
'stars_3_years': widgets_map['stars_3_years'].value,
'stars_4_years': widgets_map['stars_4_years'].value,
'stars_5_years': widgets_map['stars_5_years'].value,
'galaxies_1_years': widgets_map['galaxies_1_years'].value,
'galaxies_2_years': widgets_map['galaxies_2_years'].value,
'galaxies_3_years': widgets_map['galaxies_3_years'].value,
'galaxies_4_years': widgets_map['galaxies_4_years'].value,
'galaxies_5_years': widgets_map['galaxies_5_years'].value
}
try:
# Calculate allocations
allocation_data = calculate_dynamic_allocations(participation_counts)
bonus_data = calculate_bonus_pools(allocation_data)
final_data = calculate_final_allocations(allocation_data, bonus_data)
# Generate test output
test_output = generate_test_output(final_data)
# Add metadata
export_data = {
'metadata': {
'source': 'lockdrop-experiment.ipynb',
'scenario': 'custom',
'participation_counts': participation_counts
},
'allocations': test_output
}
# Save to file
with open(filename, 'w') as f:
json.dump(export_data, f, indent=2)
print(f"✅ Results exported to: {filename}")
print(f"📊 Total participants: {sum(participation_counts.values()):,}")
except Exception as e:
print(f"❌ Export failed: {str(e)}")
return export_current_scenario