GithubHelp home page GithubHelp logo

coin-or / grumpy Goto Github PK

View Code? Open in Web Editor NEW
61.0 9.0 22.0 2.66 MB

A Python library for visualizing algorithms for solving mathematical optimization problems.

License: Eclipse Public License 1.0

Shell 48.42% Python 39.71% Makefile 10.87% M4 1.00%

grumpy's Introduction

GrUMPy 0.95

DOI

Graphics for Understanding Mathematical Programming in Python (GrUMPy) is a Python library for visualizing various aspects of mathematical programming, including visualizations of the branch-and process, branch-and-bound trees, polyhedra, cutting plane methods, etc. The goal is clarity in implementation rather than efficiency. Most methods have an accompanying visualization and are thus appropriate for use in the classroom.

Documentation for the API is here:

https://coin-or.github.io/GrUMPy

Pypi download page is here:

https://pypi.python.org/pypi/coinor.grumpy

See below for brief documentation of usage

Installation Notes

To install, do:

pip install coinor.grumpy
  1. GrUMPy depends on GiMPy, which will be automatically installed as part of the setup. However, in order for GiMPy to visualize the branch-and-bound tree, it's necessary to install GraphViz and choose one of these additional methods for display:
  • Recommended: matplotlib and call `set_display_mode('matplotlib')
  • Python Imaging Library and call set_display_mode('PIL')
  • Call set_display_mode('file') to just write files to disk that have to then be opened manually.

It is also possible to typeset labels in LaTex and to output the graph in LaTex format using dot2tex (Warning: recent versions of dot2tex have not perfectly, your mileage may vary). After installing dot2tex, this can be done by simply calling the method write(basename='fileName', format='dot'), and then doing dot2tex --tmath fileName.dot or by calling set_display_mode('dot2tex') and then display() as usual. At the moment, the latter only seems to work with version 2.9.0dev available here. For the former method, just using easy_install dot2tex should work fine.

  1. GrUMPy also creates some visualizations with gnuplot. For tips on installing gnuplot, see below.
  2. GrUMPy can also visualize 2D polyhedra with the installation of pypolyhedron, which can be install with pip install pypolyhedron

Additional Notes for Windows Installation

  • To install Graphviz, download the installer here. Important: after installing, you must manually add the graphviz bin directory (usually C:\Program Files (x86)\Graphviz2.38\bin) to your PATH
  • To install gnuplot, download the installer here. Note that the CYGWIN version of gnuplot may not work when called from Python.

Additional Notes for Linux Installation

  • Graphviz can be installed as a package on most Linux distros, e.g., sudo apt-get install graphviz
  • Gnuplot should also be available for installation with your favorite package manager.

Additional Notes for OS X Users

  • The situation with Python on OS X is a bit of a mess. It is recommended to install python using homebrew with brew install python).
  • With homebbrew, one can also easily install graphviz (brew install graphviz) and gnuplot (brew install gnuplot)

Examples of Visualizations

Branch and bound tree

Figure 1 made with GrUMPy

Figure 2 made with GrUMPy

Figure 3 made with GrUMPy

Usage

Visualizing Branch and Bound Trees

There are two separate modes for visualizing branch and bound trees. One uses GiMPy to visualize the tree (which in turn uses GraphViz for layout). The other is to use the visualizations from the now-defunct Branch and Bound Analysis Kit (BAK), which was merged with GrUMPy. With the methods of the former BAK, one can visualize the tree in a number of ways using the venerated gnuplot (see a paper about it here).

The tree object itself can also be constructed in two different ways. The first is using the API of the BBTree class, as follows.

from coinor.grumpy import BBTree

T = BBTree()

#These can be any node attributes recognized by GraphViz
attr = {'color':'red',
        'style':'filled',
        'fillcolor':'red',
        'label':id}

T.AddOrUpdateNode(id = 0, parent_id = None,
                  branch_direction = None,
                  status = 'branched',
                  lp_bound = 100.2,
                  integer_infeasibility_count = 10,
                  integer_infeasibility_sum = 1.5,
                  condition_begin = None,
                  condition_end = None,
                  attr)

The properties of a node that are needed to visualize the tree with BAK are

  • id: The index of the node (or another unique id)
  • parent_id: The id of the parent node
  • branch_direction: For nodes other than the root node, this indicates which child (left or right) the node is of the parent. There is a somewhat arbitrary convention that when branching on variable disjunctions, the left branch is the one in which the variable's upper bond is reduced and the right branch is the one in which the variable's lower bound is increased.
  • status: The status of the node in the solution process. Can be one of
    • candidate: The node is a leaf node in the current (incomplete) tree.
    • pregnant: The node has been processed, but has not yet been branched.
    • branched: The node has been branched and is now an internal node.
    • infeasible: The node was found infeasible.
    • fathomed: The node was fathomed by bound.
    • integer: Solving the relaxation resulted in an integer solution
  • lp_bound: Bound obtained by solving the relaxation associated with the node.
  • integer_infeasibility_count: The number of variables that are fractional in the solution to the relaxation.
  • integer_infeasibility_sum: The total sum of the fractional parts of the values of the variables.
  • condition_begin: The condition number of the optimal basis for the initial LP relaxation in the node.
  • condition_end: The condition number of the optimal basis for the final LP relaxation in the node (after possibly adding cuts).

The properties related to condition number are optional and utilized only if present. For visualizing the tree with GiMPy, only the id and parent_id are required (in the future, this may change).

The second way of constructing the tree is by parsing a log file from a solver. Any solver can be instrumented to generate a file that can then be used to visualize the tree and this can even be done "live" (subject to the speed at which the visualizations can be generated). The log file must contain one line for each event type. The events are primarily those that either create or change the status of nodes and in fact, the valid event types are exactly those listed above as valid node statuses with one addition: there is also an event called heuristic corresponding to the generation of a new solution found by a heuristic. The heuristic event is the only one that does not change the status of any node, just the global bound.

Each line of the log file consists of a space-separated list of fields. The fields are as follows:

  1. Time stamp.
  2. Event type (one of the statuses listed above or heuristic.
  3. The id of the node being created or changing status.
  4. The id of the node's parent.
  5. The branch direction (R or L).

The remaining fields differ by event type.

  • heuristic: Field 6 is the bjective value of the solution.
  • infeasible: Fields 6-7 are the beginning and ending conditions numbers (optional).
  • branched: Field 6 is the LP bound, fields 7-8 are the integer infeasibility count and the sum of integer infeasibilities, fields 9-10 are the beginning and ending conditions numbers (optional).
  • candidate: Field 6 is the LP bound.
  • pregnant: Field 6 is the LP bound, fields 7-8 are the beginning and ending conditions numbers (optional). Pregnant status lines are entirely optional.
  • integer: Field 6 is the value of the solution (LP bound), fields 9-10 are the beginning and ending conditions numbers (optional).
  • fathomed: No additional fields.

A snippet from an example file might look as follows (see, e.g., p0201_SYMPHONY.vbc):

  0.288516 branched 1 0 M 7125.000000 11.368421 24 19000000 100000
  0.289231 candidate 2 1 R 7465.000000
  0.289739 candidate 3 1 L 7125.000000
  0.302334 branched 3 1 L 7125.000000 11.368421 24 4750000 4750000
  0.302859 candidate 4 3 R 7125.000000
  0.303312 candidate 5 3 L 7125.000000
  0.311040 branched 4 3 R 7465.000000 7.500000 15 500000 500000
  0.311567 candidate 6 4 R 7465.000000
  0.312036 candidate 7 4 L 7465.000000
  0.317941 branched 6 4 R 7715.000000 6.250000 13 2000000000 2000000000
  0.318476 candidate 8 6 R 7715.000000
  0.318952 candidate 9 6 L 7715.000000
   .
   .
   .
 11.512976 integer 568 522 R 7805.000000
 11.513654 fathomed 1070 569 R
 11.514258 fathomed 1066 208 R
 11.514856 fathomed 1067 208 L
   .
   .
   .

The file can be processed as follows (see, e.g., BBVis.py:

from coinor.grumpy import BBTree

bt = BBTree()
file = open('vbc.out', 'r')
for line in file:
    bt.ProcessLine(line)

To display the visualizations, do

# BAK style
bt.set_display_mode('matplotlib')
bt.display('tree')

# Graphviz style
bt.set_layout('dot')
bt.display()

Visualizing Polyhedra

GrUMPy can be used to visualize polyhedra in two dimensions using the module polyhedron (see, e.g., DisplayPolyhedronAndSolveLP.py).

from coinor.grumpy.polyhedron2D import Polyhedron2D, Figure
points = [[2.5, 4.5], [6.5, 0.5], [0.5, 1],
          [7, 5.7], [7.7, 5], [2, 0.25]]
rays = []
c = [2, 5]
opt = [7, 5.7]
loc = (opt[0]+0.1, opt[1]-0.1)
obj_val = 42.5

p = Polyhedron2D(points = points, rays = rays)
f = Figure()
f.add_polyhedron(p, label = 'Polyhedron $P$', color = 'red')
f.set_xlim([p.xlim[0], p.xlim[1]+1])
f.set_ylim([p.ylim[0], p.ylim[1]+2])
f.add_line(c, obj_val, p.xlim + [0.2, 0.8], p.ylim + [0.2, 1.8], 
           linestyle = 'dashed', color = 'black', label = "Objective Function")
f.add_point(opt, 0.04, 'red')
f.add_text(loc, r'$x^* = %s$' % str(opt))
f.show()

grumpy's People

Contributors

akeeman avatar aykutbulut avatar mattmilten avatar svigerske avatar tkralphs avatar xmunoz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

grumpy's Issues

easy_install installs an old version.

I found easy_install installed 0.84, while the current version is 0.85. And, the 0.84 version is not working.

Coorection: The version 0.84 I referred was actually 0.8.4.

wrong expressions for the calculating pseduocost score

Line 369-370 in the BranchAndBound.py

scores[i] = min(pseudo_u[i][0]*(1-var[i].varValue),
                        pseudo_d[i][0]*var[i].varValue)

should be updated to

scores[i] = min(pseudo_u[i][0]*(math.ceil(var[i].varValue)-var[i].varValue),
                pseudo_d[i][0]*(var[i].varValue- math.floor(var[i].varValue))

to accommodate for non-binary problems.

Problems generating the tree image

I tried to run the BBVis.py example to get a grasp of the tree visualization feature, but I am getting the following error from line 27

gnuplot_image = io.StringIO(bt.GenerateTreeImage())

I am getting:

File "/home/totalunimodular/.local/lib/python3.8/site-packages/coinor/grumpy/BBTree.py", line 1467, in GenerateTreeImage
return gp.communicate(input=data)[0]
File "/usr/lib/python3.8/subprocess.py", line 1024, in communicate
stdout, stderr = self._communicate(input, endtime, timeout)
File "/usr/lib/python3.8/subprocess.py", line 1846, in _communicate
input_view = memoryview(self._input)
TypeError: memoryview: a bytes-like object is required, not 'str'

After a quick google search, it seems this may be a Python 2/Python 3 issue, but I am not sure how to address it. Any insights would be appreciated.

Thanks.

local variable 'integer_infeasibility_count' referenced before assignment

Problem description

For a random generated IP problem, if I do not allow the variables to be binary, i.e., BranchAndBound(...,binary_vars = False) then I would have a bug reported as

local variable 'integer_infeasibility_count' referenced before assignment.

If I call BranchAndBound(...,binary_vars = True), then everything works.

Minimal Reproducible Example

import coinor.grumpy as gpy
problem = [(10,10,1)]
T = BBTree()
var, con, seed = problem[0]
CONSTRAINTS, VARIABLES, OBJ, MAT, RHS = gpy.GenerateRandomMIP(numVars=var,
                                                              numCons=con,
                                                              rand_seed=seed)
solution, opt_value = gpy.BranchAndBound(T,CONSTRAINTS, VARIABLES, OBJ,
                                        MAT, RHS, binary_vars = False)

Outputs from the terminal:

---------------------------------------------------------------------------
UnboundLocalError                         Traceback (most recent call last)
<ipython-input-39-5435904b9562> in <module>
      7                                                               rand_seed=seed)
      8 solution, opt_value = gpy.BranchAndBound(T,CONSTRAINTS, VARIABLES, OBJ,
----> 9                                         MAT, RHS, binary_vars = False)

~/anaconda/lib/python3.6/site-packages/coinor/grumpy/BranchAndBound.py in BranchAndBound(T, CONSTRAINTS, VARIABLES, OBJ, MAT, RHS, branch_strategy, search_strategy, complete_enumeration, display_interval, binary_vars)
    279                         'first incumbent.')
    280             T.AddOrUpdateNode(0, None, None, 'candidate', relax,
--> 281                              integer_infeasibility_count,
    282                              integer_infeasibility_sum,
    283                              label = label,

UnboundLocalError: local variable 'integer_infeasibility_count' referenced before assignment

As a side:
The above problem problem parameter (10,10,1) is based on problems given in here.
As indicated by this line, the optimal objective value is 45. By the code above with binary_vars = True gives objective value 43. If I am not messing up anything is this case, I would assume this has to do with the rand_seed, even though we are using the same value
rand_seed=1. It would be helpful if you can also check on this.

Polyhedron2D::determine_plot_size: list index out of range

Problem

When trying to plot a polyhedron, the method determine_plot_size() gives following error.

list index out of range

Minimal Reproducible example

import numpy as np
from coinor.grumpy.polyhedron2D import Polyhedron2D, Figure
A = np.array([[4, 1],[1, 4],[1, -1]])
b = np.array([[28],[27],[1]])
p = Polyhedron2D(A = A, b = b)
pI = p.make_integer_hull()
pI.determine_plot_size()

The issue is, in line 66 of the Polyhedron2D.py ,

self.hrep.adj

is a list, whose only element is a empty list. Therefore,

self.hrep.adj[self.ray_indices[0]][0]

will gives the error above.

examples and tests do not work

Scripts in examples and test directory do not work. They should import coinor.grumpy not grumpy. They still do not work even the importing is fixed.

run.py in examples directory imports src.grumpy. This does not make sense for users since they will install grumpy using "python setup.py install".

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.