|
|
| import gradio as gr |
| import pandas as pd |
| import plotly.express as px |
| import plotly.graph_objects as go |
| import random |
|
|
| |
| regions = ["Riyadh", "Makkah", "Eastern", "Madinah", "Qassim", "Asir", "Tabuk", "Hail", "Northern", |
| "Jazan", "Najran", "Bahah", "Jawf"] |
| income_bands = ["Low", "Mid", "High"] |
| property_types = ["Land", "Off-plan", "Ready", "Self-build"] |
|
|
| def default_subsidy_values(): |
| base = 300_000 |
| factor = 0.5 |
| return {(r, i, p): int((base - 100_000 * income_bands.index(i)) * factor) |
| for r in regions for i in income_bands for p in property_types} |
|
|
| def default_supply_cost_values(): |
| return {(p, r): {"Supply": random.randint(1000, 10000), "Discount (SAR)": random.randint(50_000, 300_000)} |
| for p in ["Land", "Off-plan"] for r in regions} |
|
|
| def monte_carlo_optimization(subsidies, budget_limit, target_contracts, supply_dict, interest_rate, demand_increase, n_trials=10000): |
| keys = list(subsidies.keys()) |
| best_result = None |
| best_score = float('-inf') |
| fairness_penalty_weight = 10_000_000 |
|
|
| demand_multiplier = 1 + (demand_increase / 100) |
| cost_multiplier = 1 + (interest_rate / 100) |
|
|
| for _ in range(n_trials): |
| contracts = {} |
| total_budget = 0 |
| total_contracts = 0 |
| shuffled_subs = {k: int(random.randint(150000, 400000) * cost_multiplier) for k in keys} |
| available_supply = {(r, p): supply_dict.get((p, r), 0) for (p, r) in supply_dict} |
|
|
| for k in keys: |
| r, i, p = k |
| max_supply = available_supply.get((r, p), 10000) |
| max_demand = int(max_supply * demand_multiplier) |
| max_possible_contracts = int(min((budget_limit - total_budget) // shuffled_subs[k], max_demand)) if shuffled_subs[k] > 0 else 0 |
| c = random.randint(0, max_possible_contracts) if max_possible_contracts > 0 else 0 |
| contracts[k] = c |
| total_budget += shuffled_subs[k] * c |
| total_contracts += c |
|
|
| if total_budget > budget_limit: |
| continue |
|
|
| achieved_regions = set(r for (r, i, p) in contracts if contracts[(r, i, p)] > 0) |
| achieved_income = set(i for (r, i, p) in contracts if contracts[(r, i, p)] > 0) |
| achieved_props = set(p for (r, i, p) in contracts if contracts[(r, i, p)] > 0) |
| fairness_penalty = ( |
| len(regions) - len(achieved_regions) + |
| len(income_bands) - len(achieved_income) + |
| len(property_types) - len(achieved_props) |
| ) * fairness_penalty_weight |
|
|
| score = total_contracts - fairness_penalty |
|
|
| if score > best_score: |
| best_score = score |
| best_result = (contracts, total_budget, total_contracts, shuffled_subs) |
|
|
| return best_result if best_result else ({}, 0, 0, subsidies) |
|
|
| def create_gauge_chart(value, title, max_value=100): |
| fig = go.Figure(go.Indicator( |
| mode="gauge+number", |
| value=value, |
| gauge={ |
| 'axis': {'range': [0, max_value]}, |
| 'bar': {'color': "darkblue"}, |
| 'steps': [ |
| {'range': [0, max_value * 0.5], 'color': "lightgray"}, |
| {'range': [max_value * 0.5, max_value], 'color': "lightgreen"} |
| ] |
| }, |
| title={'text': title} |
| )) |
| fig.update_layout(height=350, width=450, margin=dict(t=40, b=40, l=40, r=40)) |
| return fig |
|
|
| def build_app(): |
| supply_costs = default_supply_cost_values() |
|
|
| with gr.Blocks() as app: |
| gr.Markdown("# 🏨 Strategic Gears Housing Simulator – Auto Optimization") |
|
|
| with gr.Tab("Inputs"): |
| with gr.Row(): |
| with gr.Column(): |
| ir = gr.Slider(0, 10, 5, label="Interest Rate (%)") |
| dp = gr.Slider(0, 100, 10, label="Demand Increase (%)") |
| n = gr.Slider(100, 10000, 1000, step=100, label="Number of Simulations") |
| budget_limit = gr.Slider(10_000_000, 1_000_000_000, 500_000_000, step=10_000_000, label="Budget Limit (SAR)") |
| target_contracts = gr.Slider(1000, 20000, 5000, step=100, label="Target Contracts") |
| supply_inputs = {} |
|
|
| with gr.Accordion("Supply Inputs by Region and Property Type", open=False): |
| for p in ["Land", "Off-plan"]: |
| with gr.Accordion(p, open=False): |
| for r in regions: |
| supply_inputs[(p, r)] = gr.Slider(minimum=1, maximum=10000, |
| value=supply_costs[(p, r)]["Supply"], |
| step=1, |
| label=f"{r} {p} Supply") |
| run = gr.Button("Run Simulation") |
|
|
| with gr.Tab("Outputs"): |
| summary = gr.Markdown("Optimization summary will appear here.") |
|
|
| with gr.Row(): |
| with gr.Column(): |
| df_subsidy_policy = gr.Dataframe(label="1️⃣ Recommended Subsidy Support (SAR)") |
| subsidy_by_income_bar = gr.Plot(label="Average Subsidy by Income Band") |
| with gr.Column(): |
| df_contract_summary = gr.Dataframe(label="2️⃣ Contract Distribution") |
| contracts_by_region_bar = gr.Plot(label="Contracts by Region") |
|
|
| with gr.Row(): |
| with gr.Column(): |
| df_budget_summary = gr.Dataframe(label="3️⃣ Budget Distribution") |
| budget_util_gauge = gr.Plot(label="Budget Utilization (%)") |
| with gr.Column(): |
| df_discount_table = gr.Dataframe(label="4️⃣ Discount Table") |
| contracts_by_property_pie = gr.Plot(label="Contract Distribution by Property Type") |
| target_achievement_gauge = gr.Plot(label="Target Achievement (%)") |
| total_contracts_gauge = gr.Plot(label="Total Contracts (Scaled to Target)") |
|
|
| def run_sim(interest, demand, sims, budget, target, *supplies): |
| supply_dict = {(p, r): supplies[i] for i, (p, r) in enumerate(supply_inputs)} |
| result, total_bgt, total_con, final_subs = monte_carlo_optimization( |
| default_subsidy_values(), budget, target, supply_dict, interest, demand, sims |
| ) |
|
|
| df = pd.DataFrame([{ |
| "Region": r, "Income Band": i, "Property Type": p, |
| "Contracts": result.get((r, i, p), 0), |
| "Subsidy (SAR)": final_subs[(r, i, p)], |
| "Budget (SAR)": result.get((r, i, p), 0) * final_subs[(r, i, p)] |
| } for (r, i, p) in final_subs]) |
|
|
| subsidy_df = df[["Region", "Income Band", "Property Type", "Subsidy (SAR)"]].sort_values(by="Subsidy (SAR)", ascending=False) |
| contract_df = df[["Region", "Income Band", "Property Type", "Contracts"]].sort_values(by="Contracts", ascending=False) |
| budget_df = df[["Region", "Income Band", "Property Type", "Budget (SAR)"]].sort_values(by="Budget (SAR)", ascending=False) |
|
|
| df_discount = pd.DataFrame([{ |
| "Region": r, "Property Type": p, |
| "Discount (SAR)": random.randint(50_000, 300_000) |
| } for p in ["Land", "Off-plan"] for r in regions]) |
|
|
| summary_text = f""" |
| **Optimization Summary:** |
| - Total Budget Used: {total_bgt:,.0f} SAR |
| - Budget Utilization: {(total_bgt / budget) * 100:.1f}% |
| - Total Contracts: {total_con:,} |
| - Target Achievement: {(total_con / target) * 100:.1f}% |
| """ |
|
|
| |
| budget_util_pct = (total_bgt / budget) * 100 |
| target_achieved_pct = (total_con / target) * 100 |
| total_contracts_scaled = min(100, (total_con / target) * 100) |
|
|
| gauge_budget = create_gauge_chart(budget_util_pct, "Budget Utilization (%)") |
| gauge_target = create_gauge_chart(target_achieved_pct, "Target Achievement (%)") |
| gauge_contracts = create_gauge_chart(total_contracts_scaled, "Contracts (Scaled to Target %)") |
|
|
| |
| contracts_region = df.groupby("Region")["Contracts"].sum().reset_index() |
| bar_contract_region = px.bar(contracts_region, x="Region", y="Contracts", title="Contracts by Region") |
| min_y = 10 |
| max_y = contracts_region["Contracts"].max() * 1.1 |
| bar_contract_region.update_layout( |
| height=350, width=450, |
| yaxis=dict(title="Contracts", tickformat=",d", range=[min_y, max_y]), |
| margin=dict(t=50, b=40, l=60, r=40) |
| ) |
|
|
| |
| subsidy_income = df.groupby("Income Band")["Subsidy (SAR)"].mean().reset_index() |
| bar_subsidy_income = px.bar(subsidy_income, x="Income Band", y="Subsidy (SAR)", title="Average Subsidy by Income Band") |
| bar_subsidy_income.update_layout( |
| height=350, width=450, |
| yaxis=dict(tickprefix="SAR ", tickformat="~s"), |
| margin=dict(t=50, b=40, l=60, r=40) |
| ) |
|
|
| |
| contracts_property = df.groupby("Property Type")["Contracts"].sum().reset_index() |
| pie_property = px.pie(contracts_property, names="Property Type", values="Contracts", title="Contract Distribution by Property Type") |
| pie_property.update_layout(height=350, width=450, margin=dict(t=50, b=40, l=40, r=40)) |
|
|
| return summary_text, subsidy_df, contract_df, budget_df, df_discount, gauge_budget, gauge_target, gauge_contracts, bar_contract_region, bar_subsidy_income, pie_property |
|
|
| run.click( |
| fn=run_sim, |
| inputs=[ir, dp, n, budget_limit, target_contracts] + list(supply_inputs.values()), |
| outputs=[ |
| summary, df_subsidy_policy, df_contract_summary, df_budget_summary, df_discount_table, |
| budget_util_gauge, target_achievement_gauge, total_contracts_gauge, |
| contracts_by_region_bar, subsidy_by_income_bar, contracts_by_property_pie |
| ] |
| ) |
|
|
| return app |
|
|
| app = build_app() |
| app.launch() |
|
|