Implement MaxCutPerturber

This commit is contained in:
2025-12-08 13:21:04 -06:00
parent 9192bb02eb
commit 15cdb7e679
3 changed files with 48 additions and 153 deletions

View File

@@ -1666,17 +1666,7 @@
"cell_type": "markdown",
"id": "j49upfw2o8k",
"metadata": {},
"source": [
"### Random instance generator\n",
"\n",
"The class [MaxCutGenerator][MaxCutGenerator] can be used to generate random instances of this problem. The generator operates in two modes:\n",
"\n",
"When `fix_graph=False`, a new random Erdős-Rényi graph $G_{n,p}$ is generated for each instance, where $n$ (number of vertices) and $p$ (edge probability) are sampled from the provided probability distributions. Each edge is assigned a random weight drawn from the set $\\{-1, +1\\}$ with equal probability.\n",
"\n",
"When `fix_graph=True`, a single random graph is generated during initialization and reused across all instances. To create variations, the generator randomly flips the sign of each edge weight with probability `w_jitter`, allowing for instances with the same graph structure but different edge weight patterns.\n",
"\n",
"[MaxCutGenerator]: ../../api/problems/#miplearn.problems.maxcut.MaxCutGenerator"
]
"source": "### Random instance generator\n\nThe class [MaxCutGenerator][MaxCutGenerator] can be used to generate random instances of this problem. For each instance, the generator creates a new random Erdős-Rényi graph $G_{n,p}$, where $n$ (number of vertices) and $p$ (edge probability) are sampled from user-provided probability distributions. Each edge is assigned a random weight drawn from the set $\\{-1, +1\\}$ with equal probability.\n\nTo create multiple instances with the same graph structure but different edge weight patterns, you can use [MaxCutPerturber][MaxCutPerturber]. This class takes an existing MaxCutData instance and generates new instances by randomly flipping the sign of each edge weight with a given probability while keeping the graph structure fixed.\n\n[MaxCutGenerator]: ../../api/problems/#miplearn.problems.maxcut.MaxCutGenerator\n[MaxCutPerturber]: ../../api/problems/#miplearn.problems.maxcut.MaxCutPerturber"
},
{
"cell_type": "markdown",
@@ -1688,85 +1678,11 @@
},
{
"cell_type": "code",
"execution_count": 10,
"execution_count": null,
"id": "uge28hmv3a",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"graph edges: [(0, 2), (0, 3), (0, 4), (0, 8), (1, 2), (1, 3), (1, 5), (1, 6), (1, 9), (2, 5), (2, 9), (3, 6), (3, 7), (6, 9), (7, 8), (7, 9), (8, 9)]\n",
"weights[0]: [ 1 1 1 -1 -1 -1 -1 -1 -1 -1 1 -1 -1 1 1 -1 -1]\n",
"weights[1]: [-1 1 -1 -1 -1 1 -1 1 -1 1 -1 1 -1 -1 1 -1 1]\n",
"\n",
"Gurobi Optimizer version 13.0.0 build v13.0.0rc1 (linux64 - \"Ubuntu 22.04.5 LTS\")\n",
"\n",
"CPU model: AMD Ryzen 9 7950X 16-Core Processor, instruction set [SSE2|AVX|AVX2|AVX512]\n",
"Thread count: 16 physical cores, 16 logical processors, using up to 16 threads\n",
"\n",
"Optimize a model with 0 rows, 10 columns and 0 nonzeros (Min)\n",
"Model fingerprint: 0x005f9eac\n",
"Model has 5 linear objective coefficients\n",
"Model has 17 quadratic objective terms\n",
"Variable types: 0 continuous, 10 integer (10 binary)\n",
"Coefficient statistics:\n",
" Matrix range [0e+00, 0e+00]\n",
" Objective range [1e+00, 5e+00]\n",
" QObjective range [2e+00, 2e+00]\n",
" Bounds range [1e+00, 1e+00]\n",
" RHS range [0e+00, 0e+00]\n",
"Found heuristic solution: objective 0.0000000\n",
"Found heuristic solution: objective -3.0000000\n",
"Presolve removed 0 rows and 10 columns\n",
"Presolve time: 0.00s\n",
"Presolve: All rows and columns removed\n",
"\n",
"Explored 0 nodes (0 simplex iterations) in 0.00 seconds (0.00 work units)\n",
"Thread count was 1 (of 16 available processors)\n",
"\n",
"Solution count 2: -3 0 \n",
"No other solutions better than -3\n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective -3.000000000000e+00, best bound -3.000000000000e+00, gap 0.0000%\n",
"\n",
"User-callback calls 100, time in user-callback 0.00 sec\n"
]
}
],
"source": [
"import random\n",
"import numpy as np\n",
"from scipy.stats import uniform, randint\n",
"from miplearn.problems.maxcut import (\n",
" MaxCutGenerator,\n",
" build_maxcut_model_gurobipy,\n",
")\n",
"\n",
"# Set random seed to make example reproducible\n",
"random.seed(42)\n",
"np.random.seed(42)\n",
"\n",
"# Generate random instances with a fixed 10-node graph,\n",
"# 30% edge probability, and random weight jittering\n",
"data = MaxCutGenerator(\n",
" n=randint(low=10, high=11),\n",
" p=uniform(loc=0.3, scale=0.0),\n",
" w_jitter=0.2,\n",
" fix_graph=True,\n",
").generate(10)\n",
"\n",
"# Print the graph and weights for two instances\n",
"print(\"graph edges:\", list(data[0].graph.edges()))\n",
"print(\"weights[0]:\", data[0].weights)\n",
"print(\"weights[1]:\", data[1].weights)\n",
"print()\n",
"\n",
"# Build and optimize the first instance\n",
"model = build_maxcut_model_gurobipy(data[0])\n",
"model.optimize()"
]
"outputs": [],
"source": "import random\nimport numpy as np\nfrom scipy.stats import uniform, randint\nfrom miplearn.problems.maxcut import (\n MaxCutGenerator,\n MaxCutPerturber,\n build_maxcut_model_gurobipy,\n)\n\n# Set random seed to make example reproducible\nrandom.seed(42)\nnp.random.seed(42)\n\n# Generate a reference instance with a 10-node graph and 30% edge probability\ngenerator = MaxCutGenerator(\n n=randint(low=10, high=11),\n p=uniform(loc=0.3, scale=0.0),\n)\nreference_instance = generator.generate(1)[0]\n\n# Generate perturbed instances using the reference\nperturber = MaxCutPerturber(w_jitter=0.2)\ndata = perturber.perturb(reference_instance, 10)\n\n# Print the graph and weights for two instances\nprint(\"graph edges:\", list(data[0].graph.edges()))\nprint(\"weights[0]:\", data[0].weights)\nprint(\"weights[1]:\", data[1].weights)\nprint()\n\n# Build and optimize the first instance\nmodel = build_maxcut_model_gurobipy(data[0])\nmodel.optimize()"
}
],
"metadata": {
@@ -1790,4 +1706,4 @@
},
"nbformat": 4,
"nbformat_minor": 5
}
}