Improve stable set generator

pull/1/head
Alinson S. Xavier 6 years ago
parent 3644c59101
commit 3c9b1e2f44

@ -7,23 +7,99 @@ import pyomo.environ as pe
import networkx as nx import networkx as nx
from miplearn import Instance from miplearn import Instance
import random import random
from scipy.stats import uniform, randint, bernoulli
from scipy.stats.distributions import rv_frozen
class MaxStableSetGenerator: class MaxWeightStableSetChallengeA:
def __init__(self, graph, base_weights, perturbation_scale=1.0): """
self.graph = graph - Fixed random graph (200 vertices, 5% density)
self.base_weights = base_weights - Uniformly random weights in the [100., 125.] interval
self.perturbation_scale = perturbation_scale - 500 training instances
- 100 test instances
"""
def __init__(self):
self.generator = MaxWeightStableSetGenerator(w=uniform(loc=100., scale=25.),
n=randint(low=200, high=201),
density=uniform(loc=0.05, scale=0.0),
fix_graph=True)
def get_training_instances(self):
return self.generator.generate(500)
def get_test_instances(self):
return self.generator.generate(100)
class MaxWeightStableSetGenerator:
"""Random instance generator for the Maximum-Weight Stable Set Problem.
The generator has two modes of operation. When `fix_graph` is True, the random graph is
generated only once, during the constructor. Each instance is constructed by generating
random weights and by randomly deleting vertices and edges of this graph. When `fix_graph`
is False, a new random graph is created each time an instance is constructed.
"""
def __init__(self,
w=uniform(loc=10.0, scale=1.0),
pe=bernoulli(1.),
pv=bernoulli(1.),
n=randint(low=250, high=251),
density=uniform(loc=0.05, scale=0.05),
fix_graph=True):
"""Initializes the problem generator.
Parameters
----------
w: rv_continuous
Probability distribution for the vertex weights.
pe: rv_continuous
Probability of an edge being deleted. Only used when fix_graph=True.
pv: rv_continuous
Probability of a vertex being deleted. Only used when fix_graph=True.
n: rv_discrete
Probability distribution for the number of vertices in the random graph.
density: rv_continuous
Probability distribution for the density of the random graph.
"""
assert isinstance(w, rv_frozen), "w should be a SciPy probability distribution"
assert isinstance(pe, rv_frozen), "pe should be a SciPy probability distribution"
assert isinstance(pv, rv_frozen), "pv should be a SciPy probability distribution"
assert isinstance(n, rv_frozen), "n should be a SciPy probability distribution"
assert isinstance(density, rv_frozen), "density should be a SciPy probability distribution"
self.w = w
self.n = n
self.density = density
self.fix_graph = fix_graph
self.graph = None
if fix_graph:
self.graph = self._generate_graph()
def generate(self, n_samples): def generate(self, n_samples):
def _sample(): def _sample():
perturbation = np.random.rand(self.graph.number_of_nodes()) * self.perturbation_scale if self.graph is not None:
weights = self.base_weights + perturbation graph = self.graph
return MaxStableSetInstance(self.graph, weights) else:
graph = self._generate_graph()
weights = self.w.rvs(graph.number_of_nodes())
return MaxWeightStableSetInstance(graph, weights)
return [_sample() for _ in range(n_samples)] return [_sample() for _ in range(n_samples)]
def _generate_graph(self):
return nx.generators.random_graphs.binomial_graph(self.n.rvs(), self.density.rvs())
class MaxWeightStableSetInstance(Instance):
"""An instance of the Maximum-Weight Stable Set Problem.
Given a graph G=(V,E) and a weight w_v for each vertex v, the problem asks for a stable
set S of G maximizing sum(w_v for v in S). A stable set (also called independent set) is
a subset of vertices, no two of which are adjacent.
This is one of Karp's 21 NP-complete problems.
"""
class MaxStableSetInstance(Instance):
def __init__(self, graph, weights): def __init__(self, graph, weights):
self.graph = graph self.graph = graph
self.weights = weights self.weights = weights

@ -0,0 +1,45 @@
# MIPLearn, an extensible framework for Learning-Enhanced Mixed-Integer Optimization
# Copyright (C) 2019-2020 Argonne National Laboratory. All rights reserved.
# Written by Alinson S. Xavier <axavier@anl.gov>
from miplearn import LearningSolver
from miplearn.problems.stab import MaxWeightStableSetInstance
from miplearn.problems.stab import MaxWeightStableSetGenerator
import networkx as nx
import numpy as np
from scipy.stats import uniform, randint
def test_stab():
graph = nx.cycle_graph(5)
weights = [1., 2., 3., 4., 5.]
instance = MaxWeightStableSetInstance(graph, weights)
solver = LearningSolver()
solver.solve(instance)
assert instance.model.OBJ() == 8.
def test_stab_generator_fixed_graph():
from miplearn.problems.stab import MaxWeightStableSetGenerator
gen = MaxWeightStableSetGenerator(w=uniform(loc=50., scale=10.),
n=randint(low=10, high=11),
density=uniform(loc=0.05, scale=0.),
fix_graph=True)
instances = gen.generate(1_000)
weights = np.array([instance.weights for instance in instances])
weights_avg_actual = np.round(np.average(weights, axis=0))
weights_avg_expected = [55.0] * 10
assert list(weights_avg_actual) == weights_avg_expected
def test_stab_generator_random_graph():
from miplearn.problems.stab import MaxWeightStableSetGenerator
gen = MaxWeightStableSetGenerator(w=uniform(loc=50., scale=10.),
n=randint(low=30, high=41),
density=uniform(loc=0.5, scale=0.),
fix_graph=False)
instances = gen.generate(1_000)
n_nodes = [instance.graph.number_of_nodes() for instance in instances]
n_edges = [instance.graph.number_of_edges() for instance in instances]
assert np.round(np.mean(n_nodes)) == 35.
assert np.round(np.mean(n_edges), -1) == 300.

@ -4,27 +4,17 @@
from miplearn import LearningSolver, BenchmarkRunner from miplearn import LearningSolver, BenchmarkRunner
from miplearn.warmstart import KnnWarmStartPredictor from miplearn.warmstart import KnnWarmStartPredictor
from miplearn.problems.stab import MaxStableSetInstance, MaxStableSetGenerator from miplearn.problems.stab import MaxWeightStableSetGenerator
import networkx as nx from scipy.stats import randint
import numpy as np import numpy as np
import pyomo.environ as pe import pyomo.environ as pe
import os.path import os.path
def test_benchmark(): def test_benchmark():
graph = nx.cycle_graph(10)
base_weights = np.random.rand(10)
# Generate training and test instances # Generate training and test instances
train_instances = MaxStableSetGenerator(graph=graph, train_instances = MaxWeightStableSetGenerator(n=randint(low=25, high=26)).generate(5)
base_weights=base_weights, test_instances = MaxWeightStableSetGenerator(n=randint(low=25, high=26)).generate(3)
perturbation_scale=1.0,
).generate(5)
test_instances = MaxStableSetGenerator(graph=graph,
base_weights=base_weights,
perturbation_scale=1.0,
).generate(3)
# Training phase... # Training phase...
training_solver = LearningSolver() training_solver = LearningSolver()

@ -1,31 +0,0 @@
# MIPLearn, an extensible framework for Learning-Enhanced Mixed-Integer Optimization
# Copyright (C) 2019-2020 Argonne National Laboratory. All rights reserved.
# Written by Alinson S. Xavier <axavier@anl.gov>
from miplearn import LearningSolver
from miplearn.problems.stab import MaxStableSetInstance, MaxStableSetGenerator
import networkx as nx
import numpy as np
def test_stab():
graph = nx.cycle_graph(5)
weights = [1.0, 2.0, 3.0, 4.0, 5.0]
instance = MaxStableSetInstance(graph, weights)
solver = LearningSolver()
solver.solve(instance)
assert instance.model.OBJ() == 8.0
def test_stab_generator():
graph = nx.cycle_graph(5)
base_weights = [1.0, 2.0, 3.0, 4.0, 5.0]
instances = MaxStableSetGenerator(graph=graph,
base_weights=base_weights,
perturbation_scale=1.0,
).generate(100_000)
weights = np.array([instance.weights for instance in instances])
weights_avg = np.round(np.average(weights, axis=0), 2)
weights_std = np.round(np.std(weights, axis=0), 2)
assert list(weights_avg) == [1.50, 2.50, 3.50, 4.50, 5.50]
assert list(weights_std) == [0.29] * 5

@ -18,6 +18,7 @@ class WarmStartPredictor(ABC):
def fit(self, x_train, y_train): def fit(self, x_train, y_train):
assert isinstance(x_train, np.ndarray) assert isinstance(x_train, np.ndarray)
assert isinstance(y_train, np.ndarray) assert isinstance(y_train, np.ndarray)
y_train = y_train.astype(int)
assert y_train.shape[0] == x_train.shape[0] assert y_train.shape[0] == x_train.shape[0]
assert y_train.shape[1] == 2 assert y_train.shape[1] == 2
for i in [0,1]: for i in [0,1]:

Loading…
Cancel
Save