import gradio as gr import pandas as pd import plotly.express as px import plotly.graph_objects as go import random # Constants 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}% """ # Gauges 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 by Region Bar 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 by Income Band 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) ) # Property Type Pie 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()