{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 最优水桶问题\n",
    "\n",
    "**类别：** 非线性优化\n",
    "\n",
    "使用 OptAgent 的 Python 接口描述变量、约束与目标。\n",
    "\n",
    "问题与原始示例来源：[Hexaly Code Templates](https://www.hexaly.com/templates/optimal-bucket-problem)。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 问题描述\n",
    "\n",
    "水桶的最佳形状是什么？在 **最优水桶问题** 中，我们希望设计一个能在不超过可用表面材料的情况下最大化所能容纳流体体积的水桶。一个水桶由三个值定义：底部圆盘的半径、顶部开口的半径以及高度，分别记为 r、R 和 h。问题在于选择 r、R 和 h 的值，以在水桶表面积不超过可用材料的约束下，最大化水桶的体积。\n",
    "\n",
    "更多细节请参见 [DataGenetics](http://datagenetics.com/blog/january32015/index.html)。\n",
    "\n",
    "### 建模要点\n",
    "\n",
    "- 添加 [浮点决策变量](https://optagent.pages.dev/guide/modeling/) 来建模水桶的尺寸\n",
    "- 使用 [非线性算子](https://optagent.pages.dev/guide/modeling/) 来计算水桶的表面积和体积\n",
    "- 了解 OptAgent 的建模风格：[区分决策变量与中间表达式](https://optagent.pages.dev/guide/modeling/)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 建模思路\n",
    "\n",
    "用于建造水桶的可用材料是一个半径为 1 的平面圆盘，其表面积为 S=π。在不超出该材料面积的前提下，我们尝试构造一个能容纳最大体积的水桶。\n",
    "\n",
    "模型包含三个浮点决策变量。它们代表定义水桶形状的三个量：底部圆盘半径 r、顶部开口半径 R 以及高度 h。水桶的表面积和体积完全由这三个量确定，因此它们无需作为决策变量，而是作为中间表达式。水桶的表面积由表达式 S = π*r² + π(R+r)sqrt((R-r)²+h²) 给出。在计算之后，我们将其约束为不超过 π。然后我们可以计算水桶的体积，由表达式 V = (π*h)/3 * (R²+Rr+r²) 给出，并将其最大化。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Python 实现\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from pathlib import Path\n",
    "\n",
    "from optagent import OptModel, solve\n",
    "\n",
    "\n",
    "def main(output_file=None, time_limit=2):\n",
    "    pi = 3.14159265359\n",
    "    model = OptModel()\n",
    "\n",
    "    # Numerical decisions defining the bucket shape.\n",
    "    radius_top = model.float(0, 1)\n",
    "    radius_bottom = model.float(0, 1)\n",
    "    height = model.float(0, 1)\n",
    "\n",
    "    # Keep surface area and volume as intermediate nonlinear expressions.\n",
    "    surface = pi * radius_bottom**2 + pi * (radius_top + radius_bottom) * model.sqrt(\n",
    "        (radius_top - radius_bottom) ** 2 + height**2\n",
    "    )\n",
    "    model.constraint(surface <= pi)\n",
    "    volume = pi * height / 3 * (radius_top**2 + radius_top * radius_bottom + radius_bottom**2)\n",
    "    model.maximize(volume)\n",
    "\n",
    "    solution = solve(model, time_limit_s=float(time_limit))\n",
    "    result_text = (\n",
    "        f\"Surface = {surface.value:.6f}; Volume = {volume.value:.6f}; \"\n",
    "        f\"R = {radius_top.value:.6f}; r = {radius_bottom.value:.6f}; \"\n",
    "        f\"h = {height.value:.6f}; Status = {solution.feasible}\"\n",
    "    )\n",
    "    print(result_text)\n",
    "    if output_file is not None:\n",
    "        Path(output_file).write_text(\n",
    "            f\"{surface.value} {volume.value}\\n\"\n",
    "            f\"{radius_top.value} {radius_bottom.value} {height.value}\\n\",\n",
    "            encoding=\"utf-8\",\n",
    "        )\n",
    "    return solution\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 本地运行\n",
    "\n",
    "以下代码格演示如何调用 OptAgent 的最优水桶模型。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "solution = main(time_limit=1)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "778dfc38",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.14.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
