problems: Allow correlated arguments in random problem generators

This commit is contained in:
2025-12-08 16:08:05 -06:00
parent 485625e07f
commit 9f0fa0e500
9 changed files with 133 additions and 30 deletions

View File

@@ -3,7 +3,7 @@
# Released under the modified BSD license. See COPYING.md for more details.
from dataclasses import dataclass
from typing import List, Optional, Union
from typing import List, Optional, Union, Callable
import gurobipy as gp
import numpy as np
@@ -47,8 +47,10 @@ class MultiKnapsackGenerator:
----------
n: rv_discrete
Probability distribution for the number of items (or variables).
m: rv_discrete
Probability distribution for the number of knapsacks (or constraints).
m: rv_discrete or callable
Probability distribution for the number of knapsacks (or constraints), or a
callable that takes the numer of items and returns the number of knapsacks
(e.g., lambda n: n//3).
w: rv_continuous
Probability distribution for the item weights.
K: rv_continuous
@@ -65,7 +67,7 @@ class MultiKnapsackGenerator:
def __init__(
self,
n: rv_frozen = randint(low=100, high=101),
m: rv_frozen = randint(low=30, high=31),
m: Union[rv_frozen, Callable] = randint(low=30, high=31),
w: rv_frozen = randint(low=0, high=1000),
K: rv_frozen = randint(low=500, high=501),
u: rv_frozen = uniform(loc=0.0, scale=1.0),
@@ -73,7 +75,9 @@ class MultiKnapsackGenerator:
round: bool = True,
):
assert isinstance(n, rv_frozen), "n should be a SciPy probability distribution"
assert isinstance(m, rv_frozen), "m should be a SciPy probability distribution"
assert isinstance(m, rv_frozen) or callable(
m
), "m should be a SciPy probability distribution or callable"
assert isinstance(w, rv_frozen), "w should be a SciPy probability distribution"
assert isinstance(K, rv_frozen), "K should be a SciPy probability distribution"
assert isinstance(u, rv_frozen), "u should be a SciPy probability distribution"
@@ -92,7 +96,10 @@ class MultiKnapsackGenerator:
def generate(self, n_samples: int) -> List[MultiKnapsackData]:
def _sample() -> MultiKnapsackData:
n = self.n.rvs()
m = self.m.rvs()
if callable(self.m):
m = self.m(n)
else:
m = self.m.rvs()
w = np.array([self.w.rvs(n) for _ in range(m)])
u = self.u.rvs(n)
K = self.K.rvs()

View File

@@ -3,7 +3,7 @@
# Released under the modified BSD license. See COPYING.md for more details.
from dataclasses import dataclass
from typing import List, Optional, Union
from typing import List, Optional, Union, Callable
import gurobipy as gp
import numpy as np
@@ -58,7 +58,8 @@ class PMedianGenerator:
n
Probability distribution for the number of customer.
p
Probability distribution for the number of medians.
Probability distribution for the number of medians, or a callable that takes
the number of customers and returns the number of medians (e.g., lambda n: n//10).
demands
Probability distribution for the customer demands.
capacities
@@ -70,10 +71,23 @@ class PMedianGenerator:
x: rv_frozen = uniform(loc=0.0, scale=100.0),
y: rv_frozen = uniform(loc=0.0, scale=100.0),
n: rv_frozen = randint(low=100, high=101),
p: rv_frozen = randint(low=10, high=11),
p: Union[rv_frozen, Callable] = randint(low=10, high=11),
demands: rv_frozen = uniform(loc=0, scale=20),
capacities: rv_frozen = uniform(loc=0, scale=100),
):
assert isinstance(x, rv_frozen), "x should be a SciPy probability distribution"
assert isinstance(y, rv_frozen), "y should be a SciPy probability distribution"
assert isinstance(n, rv_frozen), "n should be a SciPy probability distribution"
assert isinstance(p, rv_frozen) or callable(
p
), "p should be a SciPy probability distribution or callable"
assert isinstance(
demands, rv_frozen
), "demands should be a SciPy probability distribution"
assert isinstance(
capacities, rv_frozen
), "capacities should be a SciPy probability distribution"
self.x = x
self.y = y
self.n = n
@@ -84,7 +98,10 @@ class PMedianGenerator:
def generate(self, n_samples: int) -> List[PMedianData]:
def _sample() -> PMedianData:
n = self.n.rvs()
p = self.p.rvs()
if callable(self.p):
p = self.p(n)
else:
p = self.p.rvs()
loc = np.array([(self.x.rvs(), self.y.rvs()) for _ in range(n)])
distances = squareform(pdist(loc))
demands = self.demands.rvs(n)

View File

@@ -3,7 +3,7 @@
# Released under the modified BSD license. See COPYING.md for more details.
from dataclasses import dataclass
from typing import List, Union
from typing import List, Union, Callable
import gurobipy as gp
import numpy as np
@@ -34,7 +34,7 @@ class SetCoverGenerator:
def __init__(
self,
n_elements: rv_frozen = randint(low=50, high=51),
n_sets: rv_frozen = randint(low=100, high=101),
n_sets: Union[rv_frozen, Callable] = randint(low=100, high=101),
costs: rv_frozen = uniform(loc=0.0, scale=100.0),
K: rv_frozen = uniform(loc=25.0, scale=0.0),
density: rv_frozen = uniform(loc=0.02, scale=0.00),
@@ -45,8 +45,9 @@ class SetCoverGenerator:
----------
n_elements: rv_discrete
Probability distribution for number of elements.
n_sets: rv_discrete
Probability distribution for number of sets.
n_sets: rv_discrete or callable
Probability distribution for number of sets, or a callable that takes
the number of elements and returns the number of sets.
costs: rv_continuous
Probability distribution for base set costs.
K: rv_continuous
@@ -57,9 +58,9 @@ class SetCoverGenerator:
assert isinstance(
n_elements, rv_frozen
), "n_elements should be a SciPy probability distribution"
assert isinstance(
n_sets, rv_frozen
), "n_sets should be a SciPy probability distribution"
assert isinstance(n_sets, rv_frozen) or callable(
n_sets
), "n_sets should be a SciPy probability distribution or callable"
assert isinstance(
costs, rv_frozen
), "costs should be a SciPy probability distribution"
@@ -75,8 +76,11 @@ class SetCoverGenerator:
def generate(self, n_samples: int) -> List[SetCoverData]:
def _sample() -> SetCoverData:
n_sets = self.n_sets.rvs()
n_elements = self.n_elements.rvs()
if callable(self.n_sets):
n_sets = self.n_sets(n_elements)
else:
n_sets = self.n_sets.rvs()
density = self.density.rvs()
incidence_matrix = np.random.rand(n_elements, n_sets) < density

View File

@@ -3,7 +3,7 @@
# Released under the modified BSD license. See COPYING.md for more details.
from dataclasses import dataclass
from typing import List, Union
from typing import List, Union, Callable
import gurobipy as gp
import numpy as np
@@ -33,7 +33,7 @@ class SetPackGenerator:
def __init__(
self,
n_elements: rv_frozen = randint(low=50, high=51),
n_sets: rv_frozen = randint(low=100, high=101),
n_sets: Union[rv_frozen, Callable] = randint(low=100, high=101),
costs: rv_frozen = uniform(loc=0.0, scale=100.0),
K: rv_frozen = uniform(loc=25.0, scale=0.0),
density: rv_frozen = uniform(loc=0.02, scale=0.00),
@@ -44,8 +44,9 @@ class SetPackGenerator:
----------
n_elements: rv_discrete
Probability distribution for number of elements.
n_sets: rv_discrete
Probability distribution for number of sets.
n_sets: rv_discrete or callable
Probability distribution for number of sets, or a callable that takes
the number of elements and returns the number of sets.
costs: rv_continuous
Probability distribution for base set costs.
K: rv_continuous
@@ -56,9 +57,9 @@ class SetPackGenerator:
assert isinstance(
n_elements, rv_frozen
), "n_elements should be a SciPy probability distribution"
assert isinstance(
n_sets, rv_frozen
), "n_sets should be a SciPy probability distribution"
assert isinstance(n_sets, rv_frozen) or callable(
n_sets
), "n_sets should be a SciPy probability distribution or callable"
assert isinstance(
costs, rv_frozen
), "costs should be a SciPy probability distribution"

View File

@@ -4,7 +4,7 @@
from dataclasses import dataclass
from math import pi
from typing import List, Optional, Union
from typing import List, Optional, Union, Callable
import gurobipy as gp
import numpy as np
@@ -39,7 +39,7 @@ class UnitCommitmentGenerator:
def __init__(
self,
n_units: rv_frozen = randint(low=1_000, high=1_001),
n_periods: rv_frozen = randint(low=72, high=73),
n_periods: Union[rv_frozen, Callable] = randint(low=72, high=73),
max_power: rv_frozen = uniform(loc=50, scale=450),
min_power: rv_frozen = uniform(loc=0.5, scale=0.25),
cost_startup: rv_frozen = uniform(loc=0, scale=10_000),
@@ -55,8 +55,9 @@ class UnitCommitmentGenerator:
----------
n_units: rv_frozen
Probability distribution for number of units.
n_periods: rv_frozen
Probability distribution for number of periods.
n_periods: rv_frozen or callable
Probability distribution for number of periods, or a callable that takes
the number of units and returns the number of periods.
max_power: rv_frozen
Probability distribution for maximum power output.
min_power: rv_frozen
@@ -74,6 +75,12 @@ class UnitCommitmentGenerator:
min_downtime: rv_frozen
Probability distribution for minimum downtime.
"""
assert isinstance(
n_units, rv_frozen
), "n_units should be a SciPy probability distribution"
assert isinstance(n_periods, rv_frozen) or callable(
n_periods
), "n_periods should be a SciPy probability distribution or callable"
self.n_units = n_units
self.n_periods = n_periods
self.max_power = max_power
@@ -87,8 +94,11 @@ class UnitCommitmentGenerator:
def generate(self, n_samples: int) -> List[UnitCommitmentData]:
def _sample() -> UnitCommitmentData:
T = self.n_periods.rvs()
G = self.n_units.rvs()
if callable(self.n_periods):
T = self.n_periods(G)
else:
T = self.n_periods.rvs()
# Generate unit parameteres
max_power = self.max_power.rvs(G)