mirror of
https://github.com/ANL-CEEESA/MIPLearn.git
synced 2025-12-09 02:48:52 -06:00
Implement MinWeightVertexCoverPerturber
This commit is contained in:
@@ -1596,7 +1596,10 @@
|
|||||||
"\n",
|
"\n",
|
||||||
"The class [MinWeightVertexCoverGenerator][MinWeightVertexCoverGenerator] can be used to generate random instances of this problem. The class accepts the same parameters and behaves in the same way as [MaxWeightStableSetGenerator][MaxWeightStableSetGenerator]. See the [stable set section](#Stable-Set) for more details on the generation process.\n",
|
"The class [MinWeightVertexCoverGenerator][MinWeightVertexCoverGenerator] can be used to generate random instances of this problem. The class accepts the same parameters and behaves in the same way as [MaxWeightStableSetGenerator][MaxWeightStableSetGenerator]. See the [stable set section](#Stable-Set) for more details on the generation process.\n",
|
||||||
"\n",
|
"\n",
|
||||||
|
"To create multiple instances with the same graph structure but different vertex weights, you can use [MinWeightVertexCoverPerturber][MinWeightVertexCoverPerturber]. This class takes an existing MinWeightVertexCoverData instance and generates new instances by applying random scaling factors to the vertex weights while keeping the graph fixed.\n",
|
||||||
|
"\n",
|
||||||
"[MinWeightVertexCoverGenerator]: ../../api/problems/#module-miplearn.problems.vertexcover\n",
|
"[MinWeightVertexCoverGenerator]: ../../api/problems/#module-miplearn.problems.vertexcover\n",
|
||||||
|
"[MinWeightVertexCoverPerturber]: ../../api/problems/#module-miplearn.problems.vertexcover\n",
|
||||||
"[MaxWeightStableSetGenerator]: ../../api/problems/#miplearn.problems.stab.MaxWeightStableSetGenerator\n",
|
"[MaxWeightStableSetGenerator]: ../../api/problems/#miplearn.problems.stab.MaxWeightStableSetGenerator\n",
|
||||||
"\n",
|
"\n",
|
||||||
"### Example"
|
"### Example"
|
||||||
@@ -1604,7 +1607,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 9,
|
"execution_count": 13,
|
||||||
"id": "5fff7afe-5b7a-4889-a502-66751ec979bf",
|
"id": "5fff7afe-5b7a-4889-a502-66751ec979bf",
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"ExecuteTime": {
|
"ExecuteTime": {
|
||||||
@@ -1618,8 +1621,8 @@
|
|||||||
"output_type": "stream",
|
"output_type": "stream",
|
||||||
"text": [
|
"text": [
|
||||||
"graph [(0, 2), (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), (8, 9)]\n",
|
"graph [(0, 2), (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), (8, 9)]\n",
|
||||||
"weights[0] [37.45 95.07 73.2 59.87 15.6 15.6 5.81 86.62 60.11 70.81]\n",
|
"weights[0] [33.78 94.78 71.97 55.15 14.32 14.33 5.41 82.5 56.7 65.79]\n",
|
||||||
"weights[1] [ 2.06 96.99 83.24 21.23 18.18 18.34 30.42 52.48 43.19 29.12]\n",
|
"weights[1] [36. 86.89 68.02 56.08 14.75 15.26 5.35 82.41 57.66 64.06]\n",
|
||||||
"\n",
|
"\n",
|
||||||
"Gurobi Optimizer version 13.0.0 build v13.0.0rc1 (linux64 - \"Ubuntu 22.04.5 LTS\")\n",
|
"Gurobi Optimizer version 13.0.0 build v13.0.0rc1 (linux64 - \"Ubuntu 22.04.5 LTS\")\n",
|
||||||
"\n",
|
"\n",
|
||||||
@@ -1627,34 +1630,34 @@
|
|||||||
"Thread count: 16 physical cores, 16 logical processors, using up to 16 threads\n",
|
"Thread count: 16 physical cores, 16 logical processors, using up to 16 threads\n",
|
||||||
"\n",
|
"\n",
|
||||||
"Optimize a model with 15 rows, 10 columns and 30 nonzeros (Min)\n",
|
"Optimize a model with 15 rows, 10 columns and 30 nonzeros (Min)\n",
|
||||||
"Model fingerprint: 0x2d2d1390\n",
|
"Model fingerprint: 0xf99bd426\n",
|
||||||
"Model has 10 linear objective coefficients\n",
|
"Model has 10 linear objective coefficients\n",
|
||||||
"Variable types: 0 continuous, 10 integer (10 binary)\n",
|
"Variable types: 0 continuous, 10 integer (10 binary)\n",
|
||||||
"Coefficient statistics:\n",
|
"Coefficient statistics:\n",
|
||||||
" Matrix range [1e+00, 1e+00]\n",
|
" Matrix range [1e+00, 1e+00]\n",
|
||||||
" Objective range [6e+00, 1e+02]\n",
|
" Objective range [5e+00, 9e+01]\n",
|
||||||
" Bounds range [1e+00, 1e+00]\n",
|
" Bounds range [1e+00, 1e+00]\n",
|
||||||
" RHS range [1e+00, 1e+00]\n",
|
" RHS range [1e+00, 1e+00]\n",
|
||||||
"Found heuristic solution: objective 301.0000000\n",
|
"Found heuristic solution: objective 283.6700000\n",
|
||||||
"Presolve removed 7 rows and 2 columns\n",
|
"Presolve removed 7 rows and 2 columns\n",
|
||||||
"Presolve time: 0.00s\n",
|
"Presolve time: 0.00s\n",
|
||||||
"Presolved: 8 rows, 8 columns, 19 nonzeros\n",
|
"Presolved: 8 rows, 8 columns, 19 nonzeros\n",
|
||||||
"Variable types: 0 continuous, 8 integer (8 binary)\n",
|
"Variable types: 0 continuous, 8 integer (8 binary)\n",
|
||||||
"\n",
|
"\n",
|
||||||
"Root relaxation: cutoff, 8 iterations, 0.00 seconds (0.00 work units)\n",
|
"Root relaxation: cutoff, 6 iterations, 0.00 seconds (0.00 work units)\n",
|
||||||
"\n",
|
"\n",
|
||||||
" Nodes | Current Node | Objective Bounds | Work\n",
|
" Nodes | Current Node | Objective Bounds | Work\n",
|
||||||
" Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time\n",
|
" Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time\n",
|
||||||
"\n",
|
"\n",
|
||||||
" 0 0 cutoff 0 301.00000 301.00000 0.00% - 0s\n",
|
" 0 0 cutoff 0 283.67000 283.67000 0.00% - 0s\n",
|
||||||
"\n",
|
"\n",
|
||||||
"Explored 1 nodes (8 simplex iterations) in 0.00 seconds (0.00 work units)\n",
|
"Explored 1 nodes (6 simplex iterations) in 0.00 seconds (0.00 work units)\n",
|
||||||
"Thread count was 16 (of 16 available processors)\n",
|
"Thread count was 16 (of 16 available processors)\n",
|
||||||
"\n",
|
"\n",
|
||||||
"Solution count 1: 301 \n",
|
"Solution count 1: 283.67 \n",
|
||||||
"\n",
|
"\n",
|
||||||
"Optimal solution found (tolerance 1.00e-04)\n",
|
"Optimal solution found (tolerance 1.00e-04)\n",
|
||||||
"Best objective 3.010000000000e+02, best bound 3.010000000000e+02, gap 0.0000%\n",
|
"Best objective 2.836700000000e+02, best bound 2.836700000000e+02, gap 0.0000%\n",
|
||||||
"\n",
|
"\n",
|
||||||
"User-callback calls 335, time in user-callback 0.00 sec\n"
|
"User-callback calls 335, time in user-callback 0.00 sec\n"
|
||||||
]
|
]
|
||||||
@@ -1666,6 +1669,7 @@
|
|||||||
"from scipy.stats import uniform, randint\n",
|
"from scipy.stats import uniform, randint\n",
|
||||||
"from miplearn.problems.vertexcover import (\n",
|
"from miplearn.problems.vertexcover import (\n",
|
||||||
" MinWeightVertexCoverGenerator,\n",
|
" MinWeightVertexCoverGenerator,\n",
|
||||||
|
" MinWeightVertexCoverPerturber,\n",
|
||||||
" build_vertexcover_model_gurobipy,\n",
|
" build_vertexcover_model_gurobipy,\n",
|
||||||
")\n",
|
")\n",
|
||||||
"\n",
|
"\n",
|
||||||
@@ -1673,13 +1677,20 @@
|
|||||||
"random.seed(42)\n",
|
"random.seed(42)\n",
|
||||||
"np.random.seed(42)\n",
|
"np.random.seed(42)\n",
|
||||||
"\n",
|
"\n",
|
||||||
"# Generate random instances with a 10-node graph,\n",
|
"# Generate a reference instance with a 10-node graph,\n",
|
||||||
"# 25% density and random weights in the [0, 100] interval.\n",
|
"# 25% density and random weights in the [0, 100] interval.\n",
|
||||||
"data = MinWeightVertexCoverGenerator(\n",
|
"generator = MinWeightVertexCoverGenerator(\n",
|
||||||
" w=uniform(loc=0.0, scale=100.0),\n",
|
" w=uniform(loc=0.0, scale=100.0),\n",
|
||||||
" n=randint(low=10, high=11),\n",
|
" n=randint(low=10, high=11),\n",
|
||||||
" p=uniform(loc=0.25, scale=0.0),\n",
|
" p=uniform(loc=0.25, scale=0.0),\n",
|
||||||
").generate(10)\n",
|
")\n",
|
||||||
|
"reference_instance = generator.generate(1)[0]\n",
|
||||||
|
"\n",
|
||||||
|
"# Generate perturbed instances using the reference\n",
|
||||||
|
"perturber = MinWeightVertexCoverPerturber(\n",
|
||||||
|
" w_jitter=uniform(loc=0.9, scale=0.1),\n",
|
||||||
|
")\n",
|
||||||
|
"data = perturber.perturb(reference_instance, 10)\n",
|
||||||
"\n",
|
"\n",
|
||||||
"# Print the graph and weights for two instances\n",
|
"# Print the graph and weights for two instances\n",
|
||||||
"print(\"graph\", data[0].graph.edges)\n",
|
"print(\"graph\", data[0].graph.edges)\n",
|
||||||
|
|||||||
@@ -12,7 +12,11 @@ from networkx import Graph
|
|||||||
from scipy.stats import uniform, randint
|
from scipy.stats import uniform, randint
|
||||||
from scipy.stats.distributions import rv_frozen
|
from scipy.stats.distributions import rv_frozen
|
||||||
|
|
||||||
from .stab import MaxWeightStableSetGenerator
|
from .stab import (
|
||||||
|
MaxWeightStableSetGenerator,
|
||||||
|
MaxWeightStableSetPerturber,
|
||||||
|
MaxWeightStableSetData,
|
||||||
|
)
|
||||||
from miplearn.solvers.gurobi import GurobiModel
|
from miplearn.solvers.gurobi import GurobiModel
|
||||||
from ..io import read_pkl_gz
|
from ..io import read_pkl_gz
|
||||||
|
|
||||||
@@ -24,12 +28,34 @@ class MinWeightVertexCoverData:
|
|||||||
|
|
||||||
|
|
||||||
class MinWeightVertexCoverGenerator:
|
class MinWeightVertexCoverGenerator:
|
||||||
|
"""Random instance generator for the Minimum-Weight Vertex Cover Problem.
|
||||||
|
|
||||||
|
Generates instances by creating a new random Erdős-Rényi graph $G_{n,p}$ for each
|
||||||
|
instance, where $n$ and $p$ are sampled from user-provided probability distributions
|
||||||
|
`n` and `p`. For each instance, the generator independently samples each $w_v$ from
|
||||||
|
the user-provided probability distribution `w`.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
w: rv_frozen = uniform(loc=10.0, scale=1.0),
|
w: rv_frozen = uniform(loc=10.0, scale=1.0),
|
||||||
n: rv_frozen = randint(low=250, high=251),
|
n: rv_frozen = randint(low=250, high=251),
|
||||||
p: rv_frozen = uniform(loc=0.05, scale=0.0),
|
p: rv_frozen = uniform(loc=0.05, scale=0.0),
|
||||||
):
|
):
|
||||||
|
"""Initialize the problem generator.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
w: rv_continuous
|
||||||
|
Probability distribution for vertex weights.
|
||||||
|
n: rv_discrete
|
||||||
|
Probability distribution for parameter $n$ in Erdős-Rényi model.
|
||||||
|
p: rv_continuous
|
||||||
|
Probability distribution for parameter $p$ in Erdős-Rényi model.
|
||||||
|
"""
|
||||||
|
assert isinstance(w, rv_frozen), "w should be a SciPy probability distribution"
|
||||||
|
assert isinstance(n, rv_frozen), "n should be a SciPy probability distribution"
|
||||||
|
assert isinstance(p, rv_frozen), "p should be a SciPy probability distribution"
|
||||||
self._generator = MaxWeightStableSetGenerator(w, n, p)
|
self._generator = MaxWeightStableSetGenerator(w, n, p)
|
||||||
|
|
||||||
def generate(self, n_samples: int) -> List[MinWeightVertexCoverData]:
|
def generate(self, n_samples: int) -> List[MinWeightVertexCoverData]:
|
||||||
@@ -39,6 +65,38 @@ class MinWeightVertexCoverGenerator:
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class MinWeightVertexCoverPerturber:
|
||||||
|
"""Perturbation generator for existing Minimum-Weight Vertex Cover instances.
|
||||||
|
|
||||||
|
Takes an existing MinWeightVertexCoverData instance and generates new instances
|
||||||
|
by applying randomization factors to the existing weights while keeping the graph fixed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
w_jitter: rv_frozen = uniform(loc=0.9, scale=0.2),
|
||||||
|
):
|
||||||
|
"""Initialize the perturbation generator.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
w_jitter: rv_continuous
|
||||||
|
Probability distribution for randomization factors applied to vertex weights.
|
||||||
|
"""
|
||||||
|
self._perturber = MaxWeightStableSetPerturber(w_jitter)
|
||||||
|
|
||||||
|
def perturb(
|
||||||
|
self,
|
||||||
|
instance: MinWeightVertexCoverData,
|
||||||
|
n_samples: int,
|
||||||
|
) -> List[MinWeightVertexCoverData]:
|
||||||
|
stab_instance = MaxWeightStableSetData(instance.graph, instance.weights)
|
||||||
|
perturbed_instances = self._perturber.perturb(stab_instance, n_samples)
|
||||||
|
return [
|
||||||
|
MinWeightVertexCoverData(s.graph, s.weights) for s in perturbed_instances
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def build_vertexcover_model_gurobipy(
|
def build_vertexcover_model_gurobipy(
|
||||||
data: Union[str, MinWeightVertexCoverData]
|
data: Union[str, MinWeightVertexCoverData]
|
||||||
) -> GurobiModel:
|
) -> GurobiModel:
|
||||||
|
|||||||
@@ -262,7 +262,7 @@ class PyomoModel(AbstractModel):
|
|||||||
if len(obj_quad) > 0:
|
if len(obj_quad) > 0:
|
||||||
nvars = len(names)
|
nvars = len(names)
|
||||||
matrix = np.zeros((nvars, nvars))
|
matrix = np.zeros((nvars, nvars))
|
||||||
for ((left_varname, right_varname), coeff) in obj_quad.items():
|
for (left_varname, right_varname), coeff in obj_quad.items():
|
||||||
assert left_varname in varname_to_idx
|
assert left_varname in varname_to_idx
|
||||||
assert right_varname in varname_to_idx
|
assert right_varname in varname_to_idx
|
||||||
left_idx = varname_to_idx[left_varname]
|
left_idx = varname_to_idx[left_varname]
|
||||||
|
|||||||
Reference in New Issue
Block a user