commit 3ef17333346f34ae159bbd1bbef77c6041df7ce6 Author: Alinson S Xavier Date: Tue Dec 17 13:20:28 2019 -0600 Initial version diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..330ca27 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +*$py.class +*.cover +*.egg +*.egg-info/ +*.log +*.manifest +*.mo +*.pot +*.py,cover +*.py[cod] +*.sage.py +*.so +*.spec +.Python +.cache +.coverage +.coverage.* +.dmypy.json +.eggs/ +.env +.hypothesis/ +.installed.cfg +.ipynb_checkpoints +.mypy_cache/ +.nox/ +.pyre/ +.pytest_cache/ +.python-version +.ropeproject +.scrapy +.spyderproject +.spyproject +.tox/ +.venv +.webassets-cache +/site +ENV/ +MANIFEST +__pycache__/ +__pypackages__/ +build/ +celerybeat-schedule +celerybeat.pid +coverage.xml +db.sqlite3 +db.sqlite3-journal +develop-eggs/ +dist/ +dmypy.json +docs/_build/ +downloads/ +eggs/ +env.bak/ +env/ +htmlcov/ +instance/ +ipython_config.py +lib/ +lib64/ +local_settings.py +nosetests.xml +parts/ +pip-delete-this-directory.txt +pip-log.txt +pip-wheel-metadata/ +profile_default/ +sdist/ +share/python-wheels/ +target/ +var/ +venv.bak/ +venv/ +wheels/ diff --git a/miplearn/__init__.py b/miplearn/__init__.py new file mode 100644 index 0000000..7248eb0 --- /dev/null +++ b/miplearn/__init__.py @@ -0,0 +1,3 @@ +# MIPLearn: A Machine-Learning Framework for Mixed-Integer Optimization +# Copyright (C) 2019-2020 Argonne National Laboratory. All rights reserved. +# Written by Alinson S. Xavier \ No newline at end of file diff --git a/miplearn/core.py b/miplearn/core.py new file mode 100644 index 0000000..3812908 --- /dev/null +++ b/miplearn/core.py @@ -0,0 +1,41 @@ +# MIPLearn: A Machine-Learning Framework for Mixed-Integer Optimization +# Copyright (C) 2019-2020 Argonne National Laboratory. All rights reserved. +# Written by Alinson S. Xavier + +from abc import ABC, abstractmethod + +class Parameters(ABC): + """ + Abstract class for holding the data that distinguishes one relevant instance of the problem + from another. + + In the knapsack problem, for example, this class could hold the number of items, their weights + and costs, as well as the size of the knapsack. Objects implementing this class are able to + convert themselves into concrete optimization model, which can be solved by a MIPSolver, or + into 1-dimensional numpy arrays, which can be given to a machine learning model. + """ + + @abstractmethod + def to_model(self): + """ + Convert the parameters into a concrete optimization model. + """ + pass + + @abstractmethod + def to_array(self): + """ + Convert the parameters into a 1-dimensional array. + + The array is used by the LearningEnhancedSolver to determine how similar two instances are. + After some normalization or embedding, it may also be used as input to the machine learning + models. It must be numerical. + + There is not necessarily a one-to-one correspondence between parameters and arrays. The + array may encode only part of the data necessary to generate a concrete optimization model. + The entries may also be reductions on the original data. For example, in the knapsack + problem, an implementation may decide to encode only the average weights, the average prices + and the size of the knapsack. This technique may be used to guarantee that arrays + correponding to instances of different sizes have the same dimension. + """ + pass diff --git a/miplearn/solvers.py b/miplearn/solvers.py new file mode 100644 index 0000000..e3bf3c0 --- /dev/null +++ b/miplearn/solvers.py @@ -0,0 +1,25 @@ +# MIPLearn: A Machine-Learning Framework for Mixed-Integer Optimization +# Copyright (C) 2019-2020 Argonne National Laboratory. All rights reserved. +# Written by Alinson S. Xavier + +import pyomo.environ as pe + +class LearningSolver: + """ + LearningSolver is a Mixed-Integer Linear Programming (MIP) solver that uses information from + previous runs to accelerate the solution of new, unseen instances. + """ + + def __init__(self): + self.parent_solver = pe.SolverFactory('cplex_persistent') + self.parent_solver.options["threads"] = 4 + + def solve(self, params): + """ + Solve the optimization problem represented by the given parameters. + The parameters and the obtained solution is recorded. + """ + model = params.to_model() + self.parent_solver.set_instance(model) + self.parent_solver.solve(tee=True) + \ No newline at end of file diff --git a/miplearn/test_stab.py b/miplearn/test_stab.py new file mode 100644 index 0000000..027b163 --- /dev/null +++ b/miplearn/test_stab.py @@ -0,0 +1,51 @@ +# MIPLearn: A Machine-Learning Framework for Mixed-Integer Optimization +# Copyright (C) 2019-2020 Argonne National Laboratory. All rights reserved. +# Written by Alinson S. Xavier + +from .solvers import LearningSolver +from .core import Parameters +import numpy as np +import pyomo.environ as pe +import networkx as nx + + +class MaxStableSetGenerator: + """Class that generates random instances of the Maximum Stable Set (MSS) Problem.""" + + def __init__(self, n_vertices, density=0.1, seed=42): + self.graph = nx.generators.random_graphs.binomial_graph(n_vertices, density, seed) + self.base_weights = np.random.rand(self.graph.number_of_nodes()) * 10 + + def generate(self): + perturbation = np.random.rand(self.graph.number_of_nodes()) * 0.1 + weights = self.base_weights + perturbation + return MaxStableSetParameters(self.graph, weights) + + +class MaxStableSetParameters(Parameters): + def __init__(self, graph, weights): + self.graph = graph + self.weights = weights + + def to_model(self): + nodes = list(self.graph.nodes) + edges = list(self.graph.edges) + model = m = pe.ConcreteModel() + m.x = pe.Var(nodes, domain=pe.Binary) + m.OBJ = pe.Objective(rule=lambda m : sum(m.x[v] * self.weights[v] for v in nodes), + sense=pe.maximize) + m.edge_eqs = pe.ConstraintList() + for edge in edges: + m.edge_eqs.add(m.x[edge[0]] + m.x[edge[1]] <= 1) + return m + + def to_array(self): + return self.weights + + +def test_stab(): + generator = MaxStableSetGenerator(n_vertices=100) + for k in range(5): + params = generator.generate() + solver = LearningSolver() + solver.solve(params) \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..008182a --- /dev/null +++ b/setup.py @@ -0,0 +1,11 @@ +from setuptools import setup + +setup( + name='miplearn', + version='0.1', + description='A Machine-Learning Framework for Mixed-Integer Optimization', + author='Alinson S. Xavier', + author_email='axavier@anl.gov', + packages=['miplearn'], + install_requires=['pyomo'], +) \ No newline at end of file