Add instructions for interactive experimentation
This commit is contained in:
parent
6b7fca23b1
commit
5a5c4508c6
134
EXPERIMENT.md
Normal file
134
EXPERIMENT.md
Normal file
@ -0,0 +1,134 @@
|
||||
# Lockdrop Allocation Experimentation
|
||||
|
||||
This guide explains how to use the interactive notebook to experiment with different lockdrop participation scenarios and understand how various distributions affect token allocations.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Python 3.x
|
||||
|
||||
## Setup
|
||||
|
||||
1. **Clone and Navigate to Directory**
|
||||
|
||||
```bash
|
||||
git clone git@git.vdb.to:LaconicNetwork/lockdrop-simulation.git
|
||||
cd lockdrop-simulation
|
||||
```
|
||||
|
||||
2. **Create Virtual Environment**
|
||||
|
||||
```bash
|
||||
python3 -m venv venv
|
||||
source venv/bin/activate
|
||||
```
|
||||
|
||||
3. **Install Dependencies**
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
## Running the Experimental Notebook
|
||||
|
||||
1. **Start Jupyter Notebook Server**
|
||||
|
||||
```bash
|
||||
jupyter notebook
|
||||
```
|
||||
|
||||
This opens your browser to <http://localhost:8888>
|
||||
|
||||
2. **Open the Experimental Notebook**
|
||||
|
||||
Navigate to and open `lockdrop-calculations.ipynb`
|
||||
|
||||
3. **Execute the Notebook**
|
||||
|
||||
Run cells in order by clicking "Run All" from `Run` tab or execute individually with Shift+Enter
|
||||
|
||||
## Using the Interactive Interface
|
||||
|
||||
### Input Controls
|
||||
|
||||
- **Star Participation**: Adjust participation numbers for stars across 1-5 year lock durations
|
||||
- **Galaxy Participation**: Adjust participation numbers for galaxies across 1-5 year lock durations
|
||||
- **Preset Scenarios**: Use the dropdown to quickly load common scenarios:
|
||||
- **Balanced**: Even distribution across all lock periods
|
||||
- **Five-year focused**: Most participants choose maximum lock duration
|
||||
- **Short-term focused**: Most participants choose shorter lock durations
|
||||
- **Low participation**: Reduced overall participation scenario
|
||||
|
||||
### Action Buttons
|
||||
|
||||
- **🔄 Calculate Allocations**: Run calculations and display comprehensive analysis
|
||||
- **💾 Export Results**: Save experimental results to timestamped JSON file
|
||||
|
||||
## Understanding the Analysis Output
|
||||
|
||||
The notebook provides detailed analysis including:
|
||||
|
||||
### 1. Participation Distribution
|
||||
- Breakdown of participants by lock period
|
||||
- Participation rates for stars and galaxies
|
||||
- Total participant counts
|
||||
|
||||
### 2. Allocation Calculations
|
||||
- Base allocations accounting for actual participation
|
||||
- Quanta-adjusted allocations per participant
|
||||
- Calculations on rounding errors resulting from quanta usage
|
||||
|
||||
### 3. Penalty System Analysis
|
||||
- Impact of lock duration on allocations before bonuses
|
||||
- Penalty rates for each lock period
|
||||
- Allocation percentages relative to maximum
|
||||
|
||||
### 4. Bonus Pool Calculations
|
||||
- How penalties redistribute to 5-year participants
|
||||
- Bonus amounts per 5-year participant
|
||||
- Total bonus pool distribution
|
||||
|
||||
### 5. Final Allocations & Verification
|
||||
- Complete allocation breakdown by lock period
|
||||
- Z tokens per block calculations
|
||||
- Allocation verification and rounding error tracking
|
||||
|
||||
### 6. Visualizations
|
||||
- Participation distribution charts
|
||||
- Allocation comparison graphs
|
||||
- Lock period analysis plots
|
||||
|
||||
## Experimenting with Scenarios
|
||||
|
||||
### Quick Start with Presets
|
||||
|
||||
1. Select a preset from the dropdown (e.g., "Five-year focused")
|
||||
2. Click "🔄 Calculate Allocations"
|
||||
3. Review the analysis output
|
||||
4. Try different presets to compare scenarios
|
||||
|
||||
### Custom Scenarios
|
||||
|
||||
1. Manually adjust participation numbers
|
||||
2. Ensure total participants don't exceed maximum available (65,280 stars, 256 galaxies)
|
||||
3. Click "🔄 Calculate Allocations"
|
||||
4. Use "💾 Export Results" to save interesting configurations
|
||||
|
||||
## Exported Results
|
||||
|
||||
Export files contain:
|
||||
- **Metadata**: Timestamp, source notebook, participation parameters
|
||||
- **Allocations**: Complete calculation results in JSON format
|
||||
- **File naming**: `lockdrop_calculations_result_YYYYMMDD_HHMMSS.json`
|
||||
|
||||
These files can be used for:
|
||||
- Further analysis
|
||||
- Sharing experimental configurations
|
||||
- Testing different scenarios
|
||||
- Integration with other tools
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- **Import errors**: Ensure all dependencies are installed via `pip install -r requirements.txt`
|
||||
- **Widget display issues**: Restart notebook kernel and re-run cells
|
||||
- **Calculation errors**: Check that participation numbers don't exceed limits
|
||||
- **Export failures**: Ensure write permissions in the notebook directory
|
||||
@ -319,3 +319,11 @@ deactivate
|
||||
# Remove virtual environment directory
|
||||
rm -rf venv
|
||||
```
|
||||
|
||||
## Interactive Experimentation
|
||||
|
||||
For independent experimentation with lockdrop allocation scenarios without running the full simulation, see [EXPERIMENT.md](./EXPERIMENT.md). This allows you to:
|
||||
|
||||
- Experiment with different participation distributions using an interactive Jupyter notebook
|
||||
- Test various scenarios (balanced, five-year focused, short-term focused, etc.)
|
||||
- Export timestamped results for analysis
|
||||
|
||||
@ -364,32 +364,31 @@ def print_allocation_calculations(allocation_data):
|
||||
'Metric': ['Adjusted Max per Star', 'Rounding Error per Star', 'Total Rounding Error', 'Rounding Error %'],
|
||||
'Value': [f"{adjusted_max_allocation_per_star:,.6f} $Z", f"{rounding_error_per_star:.9f} $Z",
|
||||
f"{total_rounding_error_stars:,.6f} $Z", f"{percentage_rounding_error_stars:.8f}%"],
|
||||
'Note': ['Final allocation', 'Per star loss', 'Goes to bonus pool', 'Of total supply']
|
||||
'Note': ['Before bonus', 'Per star loss', 'Goes to bonus pool', 'Of total supply']
|
||||
})
|
||||
|
||||
galaxy_adjusted_df = pd.DataFrame({
|
||||
'Metric': ['Adjusted Max per Galaxy', 'Rounding Error per Galaxy', 'Total Rounding Error', 'Rounding Error %'],
|
||||
'Value': [f"{adjusted_max_allocation_per_galaxy:,.6f} $Z", f"{rounding_error_per_galaxy:.9f} $Z",
|
||||
f"{total_rounding_error_galaxies:,.6f} $Z", f"{percentage_rounding_error_galaxies:.8f}%"],
|
||||
'Note': ['Final allocation', 'Per galaxy loss', 'Goes to bonus pool', 'Of total supply']
|
||||
'Note': ['Before bonus', 'Per galaxy loss', 'Goes to bonus pool', 'Of total supply']
|
||||
})
|
||||
|
||||
print("\n🎯 ADJUSTED PARTICIPANT ALLOCATIONS")
|
||||
print("\n🎯 QUANTA ADJUSTED PARTICIPANT ALLOCATIONS")
|
||||
print_table_with_borders(star_adjusted_df, "⭐ Star Adjustments")
|
||||
print_table_with_borders(galaxy_adjusted_df, "🌌 Galaxy Adjustments")
|
||||
print("\n" + "="*80)
|
||||
|
||||
|
||||
def print_penalty_analysis():
|
||||
"""Print penalty system analysis."""
|
||||
def print_penalty_analysis(allocation_data):
|
||||
"""Print penalty system analysis using values before bonus calculations."""
|
||||
print("=" * 80)
|
||||
print("⚖️ PENALTY SYSTEM ANALYSIS")
|
||||
print("=" * 80)
|
||||
|
||||
# Theoretical penalty analysis (before bonus distribution)
|
||||
base_allocations = calculate_base_allocations()
|
||||
adjusted_max_allocation_per_star = base_allocations['adjusted_max_allocation_per_star']
|
||||
adjusted_max_allocation_per_galaxy = base_allocations['adjusted_max_allocation_per_galaxy']
|
||||
# Use adjusted allocations (after penalties, before bonus distribution)
|
||||
adjusted_max_allocation_per_star = allocation_data['adjusted_max_allocation_per_star']
|
||||
adjusted_max_allocation_per_galaxy = allocation_data['adjusted_max_allocation_per_galaxy']
|
||||
|
||||
penalty_analysis_df = pd.DataFrame({
|
||||
'Lock Period': ['5 Years', '4 Years', '3 Years', '2 Years', '1 Year'],
|
||||
@ -410,10 +409,6 @@ def print_participation_summary(allocation_data):
|
||||
total_stars_locked = allocation_data['total_stars_locked']
|
||||
total_galaxies_locked = allocation_data['total_galaxies_locked']
|
||||
|
||||
print("=" * 80)
|
||||
print("📈 DYNAMIC LOCKDROP ANALYSIS")
|
||||
print("=" * 80)
|
||||
|
||||
# Consolidated participation and lock period distribution
|
||||
participation_df = pd.DataFrame({
|
||||
'Lock Period': ['1 Year', '2 Years', '3 Years', '4 Years', '5 Years', 'Total'],
|
||||
@ -423,6 +418,8 @@ def print_participation_summary(allocation_data):
|
||||
'% of Total Galaxies': [f"{galaxies_counts[year]/NUM_GALAXIES:.2%}" for year in [1, 2, 3, 4, 5]] + [f"{total_galaxies_locked/NUM_GALAXIES:.1%}"],
|
||||
'Total': [f"{stars_counts[year] + galaxies_counts[year]:,}" for year in [1, 2, 3, 4, 5]] + [f"{total_stars_locked + total_galaxies_locked:,}"],
|
||||
})
|
||||
|
||||
print("=" * 80)
|
||||
print_table_with_borders(participation_df, "🎯 PARTICIPANTS LOCK PERIOD DISTRIBUTION")
|
||||
print("\n" + "="*80)
|
||||
|
||||
@ -441,13 +438,6 @@ def print_bonus_pool_calculations(allocation_data, bonus_data):
|
||||
print("=" * 80)
|
||||
|
||||
# Star Bonus Pool Analysis
|
||||
star_bonus_components = []
|
||||
for years in [1, 2, 3, 4]:
|
||||
star_count = stars_counts[years]
|
||||
penalty = PENALTY_RATES[years]
|
||||
if star_count > 0:
|
||||
component_amount = adjusted_max_allocation_per_star * star_count * penalty
|
||||
star_bonus_components.append(f"{years}Y Stars: {component_amount:,.2f} $Z")
|
||||
|
||||
star_bonus_df = pd.DataFrame({
|
||||
'Component': ['Penalty Pool', 'Rounding Error Bonus', 'Total Star Bonus Pool',
|
||||
@ -459,13 +449,6 @@ def print_bonus_pool_calculations(allocation_data, bonus_data):
|
||||
print_table_with_borders(star_bonus_df, "⭐ STAR BONUS POOL ANALYSIS")
|
||||
|
||||
# Galaxy Bonus Pool Analysis
|
||||
galaxy_bonus_components = []
|
||||
for years in [1, 2, 3, 4]:
|
||||
galaxy_count = galaxies_counts[years]
|
||||
penalty = PENALTY_RATES[years]
|
||||
if galaxy_count > 0:
|
||||
component_amount = adjusted_max_allocation_per_galaxy * galaxy_count * penalty
|
||||
galaxy_bonus_components.append(f"{years}Y Galaxies: {component_amount:,.2f} $Z")
|
||||
|
||||
galaxy_bonus_df = pd.DataFrame({
|
||||
'Component': ['Penalty Pool', 'Rounding Error Bonus', 'Total Galaxy Bonus Pool',
|
||||
@ -476,17 +459,6 @@ def print_bonus_pool_calculations(allocation_data, bonus_data):
|
||||
})
|
||||
print_table_with_borders(galaxy_bonus_df, "🌌 GALAXY BONUS POOL ANALYSIS")
|
||||
|
||||
# Show bonus pool sources if there are penalties
|
||||
if star_bonus_components:
|
||||
print(f"\n💡 Star Bonus Pool Sources:")
|
||||
for component in star_bonus_components:
|
||||
print(f" • {component}")
|
||||
|
||||
if galaxy_bonus_components:
|
||||
print(f"\n💡 Galaxy Bonus Pool Sources:")
|
||||
for component in galaxy_bonus_components:
|
||||
print(f" • {component}")
|
||||
|
||||
print("\n" + "="*80)
|
||||
|
||||
|
||||
@ -509,8 +481,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]:.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]],
|
||||
'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 +491,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]:.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]],
|
||||
'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")
|
||||
@ -548,9 +520,9 @@ def print_final_allocations_and_verification(allocation_data, final_data):
|
||||
|
||||
def print_analysis_tables(allocation_data, bonus_data, final_data):
|
||||
"""Print comprehensive analysis tables - calls all the detailed sections."""
|
||||
print_allocation_calculations(allocation_data)
|
||||
print_penalty_analysis()
|
||||
print_participation_summary(allocation_data)
|
||||
print_allocation_calculations(allocation_data)
|
||||
print_penalty_analysis(allocation_data)
|
||||
print_bonus_pool_calculations(allocation_data, bonus_data)
|
||||
print_final_allocations_and_verification(allocation_data, final_data)
|
||||
|
||||
@ -730,17 +702,108 @@ def create_experiment_widgets():
|
||||
|
||||
galaxy_controls = widgets.VBox([galaxies_1_years, galaxies_2_years, galaxies_3_years, galaxies_4_years, galaxies_5_years])
|
||||
|
||||
# Preset scenario dropdown
|
||||
scenario_dropdown = widgets.Dropdown(
|
||||
options=[('Select Preset', '')] + [(v['description'], k) for k, v in SCENARIOS.items()],
|
||||
description='Preset:',
|
||||
style={'description_width': '60px'},
|
||||
layout={'width': '400px'}
|
||||
)
|
||||
|
||||
# Calculate button
|
||||
calculate_button = widgets.Button(description='🔄 Calculate Allocations', button_style='success', layout={'width': '200px', 'margin': '20px 0'})
|
||||
calculate_button = widgets.Button(description='🔄 Calculate Allocations', button_style='success', layout={'width': '200px', 'margin': '10px 5px'})
|
||||
|
||||
# Export button
|
||||
export_button = widgets.Button(description='💾 Export Results', button_style='info', layout={'width': '180px', 'margin': '10px 5px'})
|
||||
|
||||
# Output area
|
||||
output_area = widgets.Output()
|
||||
|
||||
# Set up preset selection handler
|
||||
def on_preset_change(change):
|
||||
if change['new'] and change['new'] != '':
|
||||
scenario = SCENARIOS[change['new']]
|
||||
params = scenario['params']
|
||||
|
||||
# Update widget values
|
||||
stars_1_years.value = params['stars_1_years']
|
||||
stars_2_years.value = params['stars_2_years']
|
||||
stars_3_years.value = params['stars_3_years']
|
||||
stars_4_years.value = params['stars_4_years']
|
||||
stars_5_years.value = params['stars_5_years']
|
||||
galaxies_1_years.value = params['galaxies_1_years']
|
||||
galaxies_2_years.value = params['galaxies_2_years']
|
||||
galaxies_3_years.value = params['galaxies_3_years']
|
||||
galaxies_4_years.value = params['galaxies_4_years']
|
||||
galaxies_5_years.value = params['galaxies_5_years']
|
||||
|
||||
scenario_dropdown.observe(on_preset_change, names='value')
|
||||
|
||||
# Set up export button handler
|
||||
def on_export_click(button=None):
|
||||
"""Handle export button click with timestamp."""
|
||||
try:
|
||||
from datetime import datetime
|
||||
from IPython.display import clear_output
|
||||
except ImportError:
|
||||
print("❌ Required libraries not available")
|
||||
return
|
||||
|
||||
# Generate timestamp for filename
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
filename = f'lockdrop_calculations_result_{timestamp}.json'
|
||||
|
||||
# Get current values from widgets
|
||||
participation_counts = {
|
||||
'stars_1_years': stars_1_years.value,
|
||||
'stars_2_years': stars_2_years.value,
|
||||
'stars_3_years': stars_3_years.value,
|
||||
'stars_4_years': stars_4_years.value,
|
||||
'stars_5_years': stars_5_years.value,
|
||||
'galaxies_1_years': galaxies_1_years.value,
|
||||
'galaxies_2_years': galaxies_2_years.value,
|
||||
'galaxies_3_years': galaxies_3_years.value,
|
||||
'galaxies_4_years': galaxies_4_years.value,
|
||||
'galaxies_5_years': 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-calculations.ipynb',
|
||||
'timestamp': timestamp,
|
||||
'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}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Export failed: {str(e)}")
|
||||
|
||||
export_button.on_click(on_export_click)
|
||||
|
||||
# Return all widgets as a dictionary
|
||||
return {
|
||||
'star_controls': star_controls,
|
||||
'galaxy_controls': galaxy_controls,
|
||||
'scenario_dropdown': scenario_dropdown,
|
||||
'calculate_button': calculate_button,
|
||||
'export_button': export_button,
|
||||
'output_area': output_area,
|
||||
'widgets': {
|
||||
'stars_1_years': stars_1_years,
|
||||
@ -856,9 +919,8 @@ def create_calculate_function(widget_dict):
|
||||
return calculate_and_display
|
||||
|
||||
|
||||
def get_preset_scenarios():
|
||||
"""Get preset scenario definitions for experimentation."""
|
||||
return {
|
||||
# Preset scenario definitions for experimentation
|
||||
SCENARIOS = {
|
||||
'balanced': {
|
||||
'description': 'Balanced distribution across all lock periods',
|
||||
'params': {
|
||||
@ -880,13 +942,6 @@ def get_preset_scenarios():
|
||||
'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': {
|
||||
@ -894,20 +949,18 @@ def get_preset_scenarios():
|
||||
'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:
|
||||
if scenario_name not in SCENARIOS:
|
||||
print(f"❌ Unknown scenario: {scenario_name}")
|
||||
return
|
||||
|
||||
scenario = scenarios[scenario_name]
|
||||
scenario = SCENARIOS[scenario_name]
|
||||
params = scenario['params']
|
||||
widgets_map = widget_dict['widgets']
|
||||
|
||||
@ -962,8 +1015,7 @@ def create_export_function(widget_dict):
|
||||
# Add metadata
|
||||
export_data = {
|
||||
'metadata': {
|
||||
'source': 'lockdrop-experiment.ipynb',
|
||||
'scenario': 'custom',
|
||||
'source': 'lockdrop-calculations.ipynb',
|
||||
'participation_counts': participation_counts
|
||||
},
|
||||
'allocations': test_output
|
||||
|
||||
Loading…
Reference in New Issue
Block a user