implemented diff from http://code.google.com/p/pulp-or/issues/detail?id=44 adding features to CPLEX_PY
77
HISTORY
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
# PuLP, Copyright J.S. Roy (js@jeannot.org), 2002-2005
|
||||||
|
# Copyright S.A.Mitchell (s.mitchell@auckland.ac.nz), 2007-
|
||||||
|
# See the LICENSE file for copyright information.
|
||||||
|
# @(#) $Jeannot: HISTORY,v 1.8 2005/05/05 09:23:51 js Exp $
|
||||||
|
1.4.9, 2011-03-30
|
||||||
|
Added support for cplex runtime licenses
|
||||||
|
Made PULP_CBC_CMD the default LP solver for linux
|
||||||
|
Included 32 and 64 bit versions of cbc
|
||||||
|
1.4.8, 2011-03-30
|
||||||
|
Overdue fix for zero coeff issue
|
||||||
|
bugfix for default cat for LpVariable.dicts
|
||||||
|
moved tests to a different file
|
||||||
|
1.4.6, 2010-01-25
|
||||||
|
Bugfix
|
||||||
|
1.4.4, 2010-01-24
|
||||||
|
CBC 2.4 cmd line solver added
|
||||||
|
CoinMP dll 1.4.0 uses trunk version
|
||||||
|
CoinMP library object now accessed through COINMP_DLL.lib
|
||||||
|
Config files now include a %(here)s syntax to identify paths
|
||||||
|
Included solvers now moved to solvers directory
|
||||||
|
1.4.2, 2009-12-31
|
||||||
|
Fixes before coin Announcement
|
||||||
|
1.4.0, 2009-10-16
|
||||||
|
Added Elastic Constraints
|
||||||
|
Added Fractional Constraints (tests yet to be added)
|
||||||
|
Added limited resolve for gurobi
|
||||||
|
Changed version numbers for coin import
|
||||||
|
Fixed COIN_CMD
|
||||||
|
Changed code to be compatible with python 2.4
|
||||||
|
1.3.08, 2009-08-08
|
||||||
|
Bugfix COINMP_DLL
|
||||||
|
1.3.07, 2009/06/26
|
||||||
|
Changes for pypi
|
||||||
|
1.3.01, 2009/06/25
|
||||||
|
Made constraints ordered dictionaries
|
||||||
|
Small changes to parameters of COIN_DLL solver
|
||||||
|
Removed string exceptions
|
||||||
|
added the constraints from other LpProblems to LpProblem.extend()
|
||||||
|
1.3.00, 2009/06/21
|
||||||
|
Added GUROBI Solver
|
||||||
|
1.23, 2009/05/25
|
||||||
|
Removed old style MEM solvers
|
||||||
|
Cleaned up CPLEX_DLL interface
|
||||||
|
Added Sequential solve function
|
||||||
|
1.22, 2009/04/03
|
||||||
|
Added Cplex IntegerOptimalTolerence setMemoryEmphsis, and clarified linux
|
||||||
|
installation instructions
|
||||||
|
1.21.02, 2008/07/29
|
||||||
|
Added epagap, and logfile in CPLEX_DLL
|
||||||
|
1.21, 2008/07/28
|
||||||
|
Added Combination and Permutation functions
|
||||||
|
Bugfix for configsolvers
|
||||||
|
Updated setup.py to start to compile everything
|
||||||
|
1.20, 2008/06/08
|
||||||
|
Certified for inclusion in Coin-Or
|
||||||
|
Spilt the solver and constant definitions into separate files
|
||||||
|
Unit Tests make more explicit
|
||||||
|
Included external definitions for CoinMP.dll
|
||||||
|
No Makefile
|
||||||
|
1.11, 2008/03/01
|
||||||
|
Contributed by Stuart Mitchell s.mitchell@auckland.ac.nz.
|
||||||
|
Contains dll solvers that are accessed with the ctypes library.
|
||||||
|
Can use the CoinMP.dll solver from the coin-or project.
|
||||||
|
Added column-wise modelling and resolve capabilities.
|
||||||
|
Wiki added with plenty of examples aphi038@ec.auckland.ac.nz
|
||||||
|
1.9, 2007/08/06:
|
||||||
|
Stuart Mitchell
|
||||||
|
Added support for cplex 10.1.0 using ctypes library
|
||||||
|
Added support for the CoinMP.dll using ctypes library
|
||||||
|
Added distutils setup
|
||||||
|
Added a configuaration file pulp.cfg
|
||||||
|
1.1, 2005/05/03:
|
||||||
|
Fix an strange interpretation of unbounded integer variables by COIN and CPLEX
|
||||||
|
LP return codes are simplified.
|
||||||
|
C interface modules for GLPK, COIN and CPLEX
|
||||||
|
Windows compatibility
|
||||||
|
1.0, 2004/02/29: First release
|
||||||
127
INSTALL
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
Installation
|
||||||
|
------------
|
||||||
|
|
||||||
|
Note that to install PuLP you must first have a working python installation as
|
||||||
|
described in `installing python`_.
|
||||||
|
|
||||||
|
PuLP requires Python >= 2.5. Though it can be made to work with Python 2.4
|
||||||
|
|
||||||
|
The latest version of PuLP can be freely obtained from coin-or_.
|
||||||
|
Please note that this version of PuLP has not been tested with operating systems
|
||||||
|
other than Microsoft Windows and Ubuntu Linux.
|
||||||
|
|
||||||
|
Easy install and pypi installation
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
By far the easiest way to install pulp is through the use of EasyInstall_ and
|
||||||
|
CheeseShop_.
|
||||||
|
|
||||||
|
* Install EasyInstall
|
||||||
|
* In windows (please make sure easy_install is on your path)::
|
||||||
|
|
||||||
|
c:\Python26\Scripts\> easy_install -U pulp
|
||||||
|
|
||||||
|
* In Linux::
|
||||||
|
|
||||||
|
$ sudo easy_install -U pulp
|
||||||
|
$ sudo pulptest #needed to get the default solver to work
|
||||||
|
|
||||||
|
* Then follow the instructions below to test your installation
|
||||||
|
|
||||||
|
To access the examples and pulp source code use the instructions below
|
||||||
|
to install from source
|
||||||
|
|
||||||
|
|
||||||
|
Windows installation from source
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
* Install python (`installing python`_)
|
||||||
|
* Download the `PuLP zipfile`_
|
||||||
|
* Extract the zipfile to a suitable location (such as the desktop - the folder will be no longer required after installation)
|
||||||
|
* Open a command prompt by clicking "Run" in the Start Menu, and type 'cmd' in the window and push enter.
|
||||||
|
* Navigate to the extracted folder with the setup file in it. [Do this by typing 'cd foldername' at the prompt, where 'cd' stands for current directory and the 'foldername' is the name of the folder to open in the path already listed to the left of the prompt. To return back to a root drive, type 'cd C:\']
|
||||||
|
* Type 'setup.py install' at the command prompt. This will install all the PuLP functions into Python's site-packages directory.
|
||||||
|
|
||||||
|
The PuLP function library is now able to be imported from any python command line. Go to IDLE or PyDev and type
|
||||||
|
|
||||||
|
>>> from pulp import *
|
||||||
|
|
||||||
|
to load in the functions. (You need to re-import the functions each time after
|
||||||
|
you close the GUI) PuLP is written in a programming language called Python, and
|
||||||
|
to use PuLP you must write Python code to describe your optimization problem.
|
||||||
|
|
||||||
|
Linux Installation
|
||||||
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
* Extract the `PuLP zipfile`_ folder to a suitable location (such as your home directory - the folder will be no longer required after installation)
|
||||||
|
* Open a command line navigate to the extracted zipfile with the setup file in it. [Do this by typing 'cd foldername' at the prompt]
|
||||||
|
* Type the following at the command prompt. This will install all the PuLP functions into Python's callable modules.
|
||||||
|
|
||||||
|
.. code-block:: sh
|
||||||
|
|
||||||
|
$ sudo python setup.py install
|
||||||
|
|
||||||
|
* install a solver for pulp to use either
|
||||||
|
* use the included 64 or 32-bit binaries cbc-32 and cbc-64
|
||||||
|
* install glpk_ debain based distributions may use the following
|
||||||
|
|
||||||
|
.. code-block:: sh
|
||||||
|
|
||||||
|
$ sudo apt-get install glpk
|
||||||
|
|
||||||
|
* install gurobi_ (free academic licenses)
|
||||||
|
* install cplex_ (and pay $$)
|
||||||
|
* or compile coinMP_ and pulp from source using buildout and copy the files
|
||||||
|
|
||||||
|
.. code-block:: sh
|
||||||
|
|
||||||
|
$ python bootstrap.py
|
||||||
|
$ bin/buildout -c solvers.cfg
|
||||||
|
$ cp parts/lib/* src/pulp/
|
||||||
|
$ sudo setup.py install
|
||||||
|
|
||||||
|
.. _glpk: http://www.gnu.org/software/glpk/
|
||||||
|
.. _CoinMP: http://projects.coin-or.org/CoinMP
|
||||||
|
.. _cplex: http://cplex.com
|
||||||
|
.. _gurobi: http://gurobi.com
|
||||||
|
|
||||||
|
Testing your PuLP installation
|
||||||
|
------------------------------
|
||||||
|
To test that that you pulp installation is working correctly please type the
|
||||||
|
following into a python interpreter and note that the output should be similar.
|
||||||
|
The output below is what you would expect if you have not installed any other
|
||||||
|
solvers and the CoinMP_ solver bundled with pulp works.
|
||||||
|
|
||||||
|
>>> import pulp
|
||||||
|
>>> pulp.pulpTestAll()
|
||||||
|
Solver pulp.pulp.COIN_MEM unavailable.
|
||||||
|
Solver pulp.pulp.COIN_CMD unavailable.
|
||||||
|
Testing continuous LP solution
|
||||||
|
Testing maximize continuous LP solution
|
||||||
|
Testing unbounded continuous LP solution
|
||||||
|
Testing MIP solution
|
||||||
|
Testing MIP relaxation
|
||||||
|
Testing feasibility problem (no objective)
|
||||||
|
Testing an infeasible problem
|
||||||
|
Testing an integer infeasible problem (Error to be fixed)
|
||||||
|
Testing column based modelling
|
||||||
|
Testing column based modelling with empty constraints
|
||||||
|
Testing dual variables and slacks reporting
|
||||||
|
Testing resolve of problem
|
||||||
|
Testing Sequential Solves
|
||||||
|
Testing fractional constraints
|
||||||
|
Testing elastic constraints (no change)
|
||||||
|
Testing elastic constraints (freebound)
|
||||||
|
Testing elastic constraints (penalty unchanged)
|
||||||
|
Testing elastic constraints (penalty unbounded)
|
||||||
|
* Solver pulp.pulp.COINMP_DLL passed.
|
||||||
|
Solver pulp.pulp.GLPK_MEM unavailable.
|
||||||
|
Solver pulp.pulp.GLPK_CMD unavailable.
|
||||||
|
Solver pulp.pulp.XPRESS unavailable.
|
||||||
|
|
||||||
|
.. _`installing python`: http://www.diveintopython.org/installing_python/index.html
|
||||||
|
.. _coin-or: https://projects.coin-or.org/PuLP
|
||||||
|
.. _EasyInstall: http://pypi.python.org/pypi/setuptools
|
||||||
|
.. _CheeseShop: http://pypi.python.org
|
||||||
|
.. _`PuLP zipfile`: http://www.coin-or.org/download/source/PuLP/
|
||||||
|
|
||||||
22
LICENSE
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
Copyright (c) 2002-2005, Jean-Sebastien Roy (js@jeannot.org)
|
||||||
|
Modifications Copyright (c) 2007- Stuart Anthony Mitchell (s.mitchell@auckland.ac.nz)
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included
|
||||||
|
in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
20
MANIFEST.in
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
include AUTHORS
|
||||||
|
include INSTALL
|
||||||
|
include LICENSE
|
||||||
|
include MANIFEST.in
|
||||||
|
include README
|
||||||
|
include VERSION
|
||||||
|
include HISTORY
|
||||||
|
include setup.py
|
||||||
|
include ez_setup.py
|
||||||
|
include bootstrap.py
|
||||||
|
include buildout.cfg
|
||||||
|
include docs.cfg
|
||||||
|
include solvers.cfg
|
||||||
|
include examples/*.py
|
||||||
|
include src/pulp/*.cfg.linux
|
||||||
|
include src/pulp/*.cfg.win
|
||||||
|
include src/pulp/*.cfg.buildout
|
||||||
|
include src/pulp/*.py
|
||||||
|
include src/pulp/solverdir/*
|
||||||
|
#include pulp-or/doc/*.pdf
|
||||||
70
README
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
|
||||||
|
# Copyright J.S. Roy (js@jeannot.org), 2003-2005
|
||||||
|
# Copyright Stuart A. Mitchell (stu@stuartmitchell.com)
|
||||||
|
# See the LICENSE file for copyright information.
|
||||||
|
|
||||||
|
PuLP is an LP modeler written in python. PuLP can generate MPS or LP files
|
||||||
|
and call GLPK[1], COIN CLP/CBC[2], CPLEX[3], and GUROBI[4] to solve linear
|
||||||
|
problems.
|
||||||
|
|
||||||
|
See the examples directory for examples.
|
||||||
|
|
||||||
|
PuLP requires Python >= 2.5.
|
||||||
|
|
||||||
|
The examples require at least a solver in your PATH or a shared library file.
|
||||||
|
|
||||||
|
Documentation is found on https://www.coin-or.org/PuLP/.
|
||||||
|
A comprehensive wiki can be found at https://www.coin-or.org/PuLP/
|
||||||
|
|
||||||
|
Use LpVariable() to create new variables. To create a variable 0 <= x <= 3
|
||||||
|
>>> x = LpVariable("x", 0, 3)
|
||||||
|
|
||||||
|
To create a variable 0 <= y <= 1
|
||||||
|
>>> y = LpVariable("y", 0, 1)
|
||||||
|
|
||||||
|
Use LpProblem() to create new problems. Create "myProblem"
|
||||||
|
>>> prob = LpProblem("myProblem", LpMinimize)
|
||||||
|
|
||||||
|
Combine variables to create expressions and constraints and add them to the
|
||||||
|
problem.
|
||||||
|
>>> prob += x + y <= 2
|
||||||
|
|
||||||
|
If you add an expression (not a constraint), it will
|
||||||
|
become the objective.
|
||||||
|
>>> prob += -4*x + y
|
||||||
|
|
||||||
|
Choose a solver and solve the problem. ex:
|
||||||
|
>>> status = prob.solve(GLPK(msg = 0))
|
||||||
|
|
||||||
|
Display the status of the solution
|
||||||
|
>>> LpStatus[status]
|
||||||
|
'Optimal'
|
||||||
|
|
||||||
|
You can get the value of the variables using value(). ex:
|
||||||
|
>>> value(x)
|
||||||
|
2.0
|
||||||
|
|
||||||
|
Exported Classes:
|
||||||
|
- LpProblem -- Container class for a Linear programming problem
|
||||||
|
- LpVariable -- Variables that are added to constraints in the LP
|
||||||
|
- LpConstraint -- A constraint of the general form
|
||||||
|
a1x1+a2x2 ...anxn (<=, =, >=) b
|
||||||
|
- LpConstraintVar -- Used to construct a column of the model in column-wise
|
||||||
|
modelling
|
||||||
|
|
||||||
|
Exported Functions:
|
||||||
|
- value() -- Finds the value of a variable or expression
|
||||||
|
- lpSum() -- given a list of the form [a1*x1, a2x2, ..., anxn] will construct
|
||||||
|
a linear expression to be used as a constraint or variable
|
||||||
|
- lpDot() --given two lists of the form [a1, a2, ..., an] and
|
||||||
|
[ x1, x2, ..., xn] will construct a linear epression to be used
|
||||||
|
as a constraint or variable
|
||||||
|
|
||||||
|
Comments, bug reports, patches and suggestions are welcome.
|
||||||
|
pulp-or-discuss@googlegroups.com
|
||||||
|
|
||||||
|
References:
|
||||||
|
[1] http://www.gnu.org/software/glpk/glpk.html
|
||||||
|
[2] http://www.coin-or.org/
|
||||||
|
[3] http://www.cplex.com/
|
||||||
|
[4] http://www.gurobi.com/
|
||||||
34
ROADMAP
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
Roadmap for Pulp-or
|
||||||
|
===================
|
||||||
|
|
||||||
|
Version 1.0
|
||||||
|
-----------
|
||||||
|
Original Pulp version written and conceived by Jean-Sebastien Roy js@jeannot.org
|
||||||
|
|
||||||
|
Version 1.1
|
||||||
|
-----------
|
||||||
|
Contributed by Stuart Mitchell s.mitchell@auckland.ac.nz.
|
||||||
|
Contains dll solvers that are accessed with the ctypes library.
|
||||||
|
Can use the CoinMP.dll solver from the coin-or project.
|
||||||
|
Added column-wise modelling and resolve capabilities.
|
||||||
|
Wiki added with plenty of examples aphi038@ec.auckland.ac.nz
|
||||||
|
|
||||||
|
Version 1.20
|
||||||
|
------------
|
||||||
|
Certified for inclusion in Coin-Or
|
||||||
|
Spilt the solver and constant definitions into separate files
|
||||||
|
Unit Tests make more explicit
|
||||||
|
Included external definitions for CoinMP.dll
|
||||||
|
No Makefile
|
||||||
|
|
||||||
|
Version 1.3
|
||||||
|
------------
|
||||||
|
Added GUROBI solver
|
||||||
|
Removed the older MEM solvers
|
||||||
|
|
||||||
|
Future
|
||||||
|
------
|
||||||
|
Discuss language syntax looking towards removing C alike constructs and make
|
||||||
|
better use of Python namespace conventions
|
||||||
|
Integrate branch and cut within Pulp
|
||||||
|
Integrate with other python OR projects eg Pyomo wehart@sandia.gov and POAMS leo@sie.arizona.edu
|
||||||
113
bootstrap.py
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# Copyright (c) 2006 Zope Corporation and Contributors.
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# This software is subject to the provisions of the Zope Public License,
|
||||||
|
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||||||
|
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||||||
|
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||||||
|
# FOR A PARTICULAR PURPOSE.
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
"""Bootstrap a buildout-based project
|
||||||
|
|
||||||
|
Simply run this script in a directory containing a buildout.cfg.
|
||||||
|
The script accepts buildout command-line options, so you can
|
||||||
|
use the -c option to specify an alternate configuration file.
|
||||||
|
|
||||||
|
$Id$
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os, shutil, sys, tempfile, urllib2
|
||||||
|
from optparse import OptionParser
|
||||||
|
|
||||||
|
tmpeggs = tempfile.mkdtemp()
|
||||||
|
|
||||||
|
is_jython = sys.platform.startswith('java')
|
||||||
|
|
||||||
|
# parsing arguments
|
||||||
|
parser = OptionParser()
|
||||||
|
parser.add_option("-v", "--version", dest="version",
|
||||||
|
help="use a specific zc.buildout version")
|
||||||
|
parser.add_option("-d", "--distribute",
|
||||||
|
action="store_true", dest="distribute", default=False,
|
||||||
|
help="Use Disribute rather than Setuptools.")
|
||||||
|
|
||||||
|
options, args = parser.parse_args()
|
||||||
|
|
||||||
|
if options.version is not None:
|
||||||
|
VERSION = '==%s' % options.version
|
||||||
|
else:
|
||||||
|
VERSION = ''
|
||||||
|
|
||||||
|
USE_DISTRIBUTE = options.distribute
|
||||||
|
args = args + ['bootstrap']
|
||||||
|
|
||||||
|
to_reload = False
|
||||||
|
try:
|
||||||
|
import pkg_resources
|
||||||
|
if not hasattr(pkg_resources, '_distribute'):
|
||||||
|
to_reload = True
|
||||||
|
raise ImportError
|
||||||
|
except ImportError:
|
||||||
|
ez = {}
|
||||||
|
if USE_DISTRIBUTE:
|
||||||
|
exec urllib2.urlopen('http://python-distribute.org/distribute_setup.py'
|
||||||
|
).read() in ez
|
||||||
|
ez['use_setuptools'](to_dir=tmpeggs, download_delay=0, no_fake=True)
|
||||||
|
else:
|
||||||
|
exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py'
|
||||||
|
).read() in ez
|
||||||
|
ez['use_setuptools'](to_dir=tmpeggs, download_delay=0)
|
||||||
|
|
||||||
|
if to_reload:
|
||||||
|
reload(pkg_resources)
|
||||||
|
else:
|
||||||
|
import pkg_resources
|
||||||
|
|
||||||
|
if sys.platform == 'win32':
|
||||||
|
def quote(c):
|
||||||
|
if ' ' in c:
|
||||||
|
return '"%s"' % c # work around spawn lamosity on windows
|
||||||
|
else:
|
||||||
|
return c
|
||||||
|
else:
|
||||||
|
def quote (c):
|
||||||
|
return c
|
||||||
|
|
||||||
|
cmd = 'from setuptools.command.easy_install import main; main()'
|
||||||
|
ws = pkg_resources.working_set
|
||||||
|
|
||||||
|
if USE_DISTRIBUTE:
|
||||||
|
requirement = 'distribute'
|
||||||
|
else:
|
||||||
|
requirement = 'setuptools'
|
||||||
|
|
||||||
|
if is_jython:
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
assert subprocess.Popen([sys.executable] + ['-c', quote(cmd), '-mqNxd',
|
||||||
|
quote(tmpeggs), 'zc.buildout' + VERSION],
|
||||||
|
env=dict(os.environ,
|
||||||
|
PYTHONPATH=
|
||||||
|
ws.find(pkg_resources.Requirement.parse(requirement)).location
|
||||||
|
),
|
||||||
|
).wait() == 0
|
||||||
|
|
||||||
|
else:
|
||||||
|
assert os.spawnle(
|
||||||
|
os.P_WAIT, sys.executable, quote (sys.executable),
|
||||||
|
'-c', quote (cmd), '-mqNxd', quote (tmpeggs), 'zc.buildout' + VERSION,
|
||||||
|
dict(os.environ,
|
||||||
|
PYTHONPATH=
|
||||||
|
ws.find(pkg_resources.Requirement.parse(requirement)).location
|
||||||
|
),
|
||||||
|
) == 0
|
||||||
|
|
||||||
|
ws.add_entry(tmpeggs)
|
||||||
|
ws.require('zc.buildout' + VERSION)
|
||||||
|
import zc.buildout.buildout
|
||||||
|
zc.buildout.buildout.main(args)
|
||||||
|
shutil.rmtree(tmpeggs)
|
||||||
14
buildout.cfg
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
[buildout]
|
||||||
|
develop = .
|
||||||
|
parts = pythonpulp scripts
|
||||||
|
|
||||||
|
[pythonpulp]
|
||||||
|
recipe = zc.recipe.egg
|
||||||
|
interpreter = pythonpulp
|
||||||
|
eggs = pulp
|
||||||
|
|
||||||
|
[scripts]
|
||||||
|
recipe = zc.recipe.egg:scripts
|
||||||
|
eggs = pulp
|
||||||
|
|
||||||
|
|
||||||
2369
doc/KPyCon2009/IEEEtran.bst
Normal file
BIN
doc/KPyCon2009/PulpForPythonProgrammers.pdf
Normal file
343
doc/KPyCon2009/PulpForPythonProgrammers.tex
Normal file
@@ -0,0 +1,343 @@
|
|||||||
|
% Document History:
|
||||||
|
%
|
||||||
|
%-----------------------------------------------------------------------
|
||||||
|
\documentclass[a4paper,oneside]{arlimsTPPM}
|
||||||
|
%-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
%--- Settings for the ARLIMS document class ---
|
||||||
|
%\setcounter{page}{8} % The starting page
|
||||||
|
|
||||||
|
%--- This part of the preamble can be fiddled with according to our needs ---
|
||||||
|
|
||||||
|
% Some packages that we like and want to use:
|
||||||
|
\usepackage{cite}
|
||||||
|
\usepackage{url}
|
||||||
|
\usepackage{graphicx}
|
||||||
|
\usepackage{color}
|
||||||
|
\usepackage[T1]{fontenc}
|
||||||
|
\usepackage[scaled]{beramono}
|
||||||
|
|
||||||
|
% For typesetting listings.
|
||||||
|
\usepackage{listings}
|
||||||
|
\lstset{% Set some listing parameters.
|
||||||
|
numbers=left,
|
||||||
|
numberstyle=\sffamily\scriptsize,
|
||||||
|
numberblanklines=false,
|
||||||
|
basicstyle=\ttfamily\footnotesize,
|
||||||
|
showstringspaces=false,
|
||||||
|
backgroundcolor=\color[rgb]{0.9,0.9,0.9},
|
||||||
|
%frame=tb,
|
||||||
|
%xleftmargin=4.3ex,
|
||||||
|
language=Python
|
||||||
|
}
|
||||||
|
\usepackage{hyperref}
|
||||||
|
|
||||||
|
% The path(s) to the graphics files:
|
||||||
|
\graphicspath{{eps/}{pdf/}}
|
||||||
|
|
||||||
|
% correct bad hyphenation here
|
||||||
|
%\hyphenation{cor-res-pon-ding net-works}
|
||||||
|
|
||||||
|
% This makes line breaks look prettier
|
||||||
|
\sloppy
|
||||||
|
|
||||||
|
% Conventions for typesetting vectors and matrices.
|
||||||
|
\renewcommand{\vec}[1]{\boldsymbol{#1}}
|
||||||
|
\newcommand{\mat}[1]{\boldsymbol{#1}}
|
||||||
|
% If \boldsymbol doesn't work, try \pmb (poor man's bold, which
|
||||||
|
% "multistrikes" every letter with small offsets.
|
||||||
|
|
||||||
|
%--- Preamble definitions for actual content ---
|
||||||
|
|
||||||
|
\title{An Introduction to pulp for Python Programmers}
|
||||||
|
\author{Stuart Mitchell}
|
||||||
|
|
||||||
|
\institute{
|
||||||
|
\begin{minipage}[t]{.45\textwidth}\centering
|
||||||
|
Light Metals Research Centre\\
|
||||||
|
University of Auckland\\
|
||||||
|
Auckland, New Zealand\\
|
||||||
|
\textnormal{\texttt{\small s.mitchell@auckland.ac.nz}}
|
||||||
|
\end{minipage}}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
%-------------------------------------------------------------------------
|
||||||
|
\begin{document}
|
||||||
|
|
||||||
|
% Put a proper title on the first page:
|
||||||
|
\maketitle
|
||||||
|
|
||||||
|
%-------------------------------------------------------------------------
|
||||||
|
\begin{abstract}
|
||||||
|
Pulp-or (referred to as pulp for the rest of this paper) is a linear programming framework in Python. Pulp is licensed under a modified BSD license. The aim of pulp is to allow an Operations Research (OR) practitioner or programmer to express Linear Programming (LP), and Integer Programming (IP) models in python in a way similar to the conventional mathematical notation. Pulp
|
||||||
|
will also solve these problems using a variety of free and non-free LP solvers. Pulp models an LP in a natural and pythonic manner.
|
||||||
|
This paper is aimed at the python programmer who may wish to use pulp in their code. As such this paper contains a short introduction to LP models and their uses.
|
||||||
|
|
||||||
|
% ARLIMS style
|
||||||
|
\paragraph{Keywords:} Linear programming; Operations Research; pulp-or.
|
||||||
|
\end{abstract}
|
||||||
|
|
||||||
|
%-------------------------------------------------------------------------
|
||||||
|
\section{Introduction}
|
||||||
|
|
||||||
|
Operations Research is known by the catch phrase ``The science of better''. From the website \cite{scienceofbetter} we find this brief description
|
||||||
|
|
||||||
|
\begin{quote}
|
||||||
|
In a nutshell, operations research (O.R.) is the discipline of applying advanced analytical methods to help make better decisions.
|
||||||
|
\end{quote}
|
||||||
|
|
||||||
|
The particular area of operations research where pulp is useful is the development and modelling of Linear Programming (LP) and Integer Programming (IP) problems. Mathematically, an LP problem is to find a point in a n-dimensional linearly constrained region that maximises a given linear objective function. IP is an LP where the solution must contain discrete variables which take an integer value at the solution, a common special case of an integer variable is a binary variable which must be either 0 or 1 at the solution.
|
||||||
|
|
||||||
|
In general terms, an LP can describe a problem were decisions must be made (for example, the quantity of each ingredient in a can of cat food, detailed in section \ref{sec:whiskas}). These decisions are constrained by the properties of the problem that they model (the total weight of ingredients must be 100 grams and dietary requirements must be met). The quality of a solution is determined by some sort of cost (the total dollar cost to produce the can) and you seek to minimise or maximise the cost subject to the constraints.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
\section{A Brief introduction to Linear Programming}
|
||||||
|
\label{sect:LP}
|
||||||
|
|
||||||
|
In this section we will introduce the reader to the structure of an LP problem and show the syntax used to formulate these models in pulp. The following is a example LP motivated by a real-world case-study. In general, problems of this sort are called `diet' or `blending' problems. An extensive set of case studies (including those below) can be found in \cite{pulpwiki}.
|
||||||
|
|
||||||
|
\subsection{The Whiskas Cat Food Problem (whiskas.py)}
|
||||||
|
\label{sec:whiskas}
|
||||||
|
|
||||||
|
\begin{figure}[h]
|
||||||
|
\centering
|
||||||
|
\includegraphics{images/whiskas_label.jpg}
|
||||||
|
\caption{A Whiskas cat food label.}
|
||||||
|
\end{figure}
|
||||||
|
Whiskas cat food, shown above, is manufactured by Uncle Ben's. Uncle Ben's want to produce their cat food products as cheaply as possible while ensuring they meet the stated nutritional analysis requirements shown on the cans. Thus they want to vary the quantities of each ingredient used (the main ingredients being chicken, beef, mutton, rice, wheat and gel) while still meeting their nutritional standards.
|
||||||
|
|
||||||
|
\begin{figure}[h]
|
||||||
|
\centering
|
||||||
|
\includegraphics[scale=0.5]{images/whiskas_ingredients.jpg}\includegraphics[scale=0.5]{images/whiskas_nutrition.jpg}
|
||||||
|
\caption{Detail of ingredients and nutritional requirements.}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
\begin{figure}[h]
|
||||||
|
\centering
|
||||||
|
\includegraphics[scale=0.6]{images/whiskas_blend.jpg}
|
||||||
|
\caption{The quantity of each ingredient must be determined.}
|
||||||
|
\end{figure}
|
||||||
|
The costs of the chicken, beef, and mutton are \$0.013, \$0.008 and \$0.010 respectively, while the costs of the rice, wheat and gel are \$0.002, \$0.005 and \$0.001 respectively. (All costs are per gram.) For this exercise we will ignore the vitamin and mineral ingredients. (Any costs for these are likely to be very small anyway.)
|
||||||
|
Each ingredient contributes to the total weight of protein, fat, fibre and salt in the final product which must be 100g (very convenient). The contributions (in grams) per gram of ingredient are given in the table below.
|
||||||
|
|
||||||
|
\begin{table}
|
||||||
|
\centering
|
||||||
|
\begin{tabular}{|l|l|l|l|l|}\hline
|
||||||
|
& Protein & Fat & Fibre & Salt\\\hline
|
||||||
|
Chicken & 0.100 & 0.080 & 0.001 & 0.002\\
|
||||||
|
Beef & 0.200 & 0.100 & 0.005 & 0.005\\
|
||||||
|
Rice & 0.000 & 0.010 & 0.100 & 0.008\\
|
||||||
|
Wheat bran & 0.040 & 0.010 & 0.150 & 0.000\\\hline
|
||||||
|
\end{tabular}
|
||||||
|
\caption{Table of ingredient compositions.}
|
||||||
|
\end{table}
|
||||||
|
|
||||||
|
A Linear Program consists of three main parts. These are the variable definitions, the objective function and the constraints.
|
||||||
|
|
||||||
|
\subsubsection{Variable definition}
|
||||||
|
In this problem the variables will be defined as the quantity (in grams) of each ingredient to include in the can. These variables will be continuous and will be able to take any non-negative value. Mathematically, we define set $I$ as the set of ingredients then create an $x$ variable indexed by $I$
|
||||||
|
|
||||||
|
\begin{eqnarray*}
|
||||||
|
I &=& \{chicken, beef, mutton, rice, wheat, gel\}\\
|
||||||
|
x_i &\ge& 0 \quad \quad i \in I.
|
||||||
|
\end{eqnarray*}
|
||||||
|
|
||||||
|
\lstinputlisting[linerange={4-12}]{code/whiskas.py}
|
||||||
|
|
||||||
|
\subsubsection{Objective Function}
|
||||||
|
The objective function is to minimise the cost of production for each can.
|
||||||
|
\[
|
||||||
|
\min \sum_i c_i x_i.
|
||||||
|
\]
|
||||||
|
Where:
|
||||||
|
\begin{itemize}
|
||||||
|
\item $c_i$ is the cost per gram of ingredient $i$.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\lstinputlisting[linerange={14-17}]{code/whiskas.py}
|
||||||
|
|
||||||
|
\subsubsection{Constraints}
|
||||||
|
Constraints serve to control the total mass of the ingredients and to model the protein, fat, salt and fibre requirements.
|
||||||
|
|
||||||
|
\begin{align*}
|
||||||
|
\sum_i x_i &= 100 \; \text{\hspace{19 mm}can mass} \\
|
||||||
|
\sum_i p_i x_i &\ge 8.0 \; \text{\hspace{20 mm}protein} \\
|
||||||
|
\sum_i f_i x_i &\ge 6.0 \; \text{\hspace{20 mm}fat} \\
|
||||||
|
\sum_i b_i x_i &\le 2.0 \; \text{\hspace{20 mm}fibre} \\
|
||||||
|
\sum_i s_i x_i &\le 0.4 \; \text{\hspace{20 mm}salt}.
|
||||||
|
\end{align*}
|
||||||
|
Where:
|
||||||
|
\begin{itemize}
|
||||||
|
\item $p_i$ is the protein per gram of ingredient $i$;
|
||||||
|
\item $f_i$ is the fat per gram of ingredient $i$;
|
||||||
|
\item $b_i$ is the fibre per gram of ingredient $i$;
|
||||||
|
\item $s_i$ is the salt per gram of ingredient $i$.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\lstinputlisting[linerange={18-29}]{code/whiskas.py}
|
||||||
|
|
||||||
|
The pulp model must now be solved with some third party optimisation software.
|
||||||
|
Pulp currently supports solving with coin-or \cite{coin-or}, glpk \cite{glpk},
|
||||||
|
CPLEX \cite{cplex} and Gurobi \cite{gurobi}.
|
||||||
|
The pulp download includes compiled versions of the coin-or solver for ubuntu and windows computers.
|
||||||
|
|
||||||
|
\lstinputlisting[linerange={31-37}]{code/whiskas.py}
|
||||||
|
|
||||||
|
\section{Other Example LP models}
|
||||||
|
|
||||||
|
Modelling the diet problem is not the only application of linear programming. Other examples include:
|
||||||
|
\begin{itemize}
|
||||||
|
\item the transportation problem,
|
||||||
|
\item the set partitioning problem,
|
||||||
|
\item the assignment problem,
|
||||||
|
\item the knapsack problem.
|
||||||
|
\end{itemize}
|
||||||
|
In this section we will give short examples of the first two problems modelled in pulp together with a brief description.
|
||||||
|
|
||||||
|
\subsection{The Transportation Problem (beerdistribution.py)}
|
||||||
|
A transportation problem involves that shipment of items from a set of sources
|
||||||
|
to a set of sinks (the sources and sinks must be disjoint sets), the problem seeks
|
||||||
|
to minimise the cost of shipping. In this case-study
|
||||||
|
crates of beer must be shipped from two breweries to five bars.
|
||||||
|
|
||||||
|
\begin{figure}[h]
|
||||||
|
\centering
|
||||||
|
\includegraphics[scale = 0.3]{images/beerdistribution.png}
|
||||||
|
\caption{The brewery and bar capacities and possible routes.}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
|
||||||
|
The problem is created.
|
||||||
|
\lstinputlisting[linerange={37-38}]{code/beerdistribution.py}
|
||||||
|
|
||||||
|
A list of possible routes for transportation is created:
|
||||||
|
\lstinputlisting[linerange={40-41}]{code/beerdistribution.py}
|
||||||
|
|
||||||
|
The variables created determine the amount shipped on each route. The variables
|
||||||
|
are defined as \lstinline{pulp.LpInteger} therefore solutions must not
|
||||||
|
ship fractional numbers of crates.
|
||||||
|
\lstinputlisting[linerange={43-47}]{code/beerdistribution.py}
|
||||||
|
|
||||||
|
The objective is to minimise the total shipping cost of the solution.
|
||||||
|
\lstinputlisting[linerange={48-50}]{code/beerdistribution.py}
|
||||||
|
|
||||||
|
These constraints ensure that amount shipped from each brewery is less
|
||||||
|
than the supply available. The names given to the constraints will be preserved
|
||||||
|
when an `.lp' file is created.
|
||||||
|
\lstinputlisting[linerange={52-55}]{code/beerdistribution.py}
|
||||||
|
|
||||||
|
These constraints ensure that amount shipped to each bar is greater
|
||||||
|
than the demand of the bar. These could also be equality constraints, depending
|
||||||
|
on how the problem is modelled.
|
||||||
|
\lstinputlisting[linerange={57-60}]{code/beerdistribution.py}
|
||||||
|
|
||||||
|
\subsection{The Set Partitioning Problem (wedding.py)}
|
||||||
|
A set partitioning problem determines how the items in one set (S) can be partitioned into smaller
|
||||||
|
subsets. All items in S must be contained in one and only one partition. Related problems are:
|
||||||
|
\begin{itemize}
|
||||||
|
\item set packing - all items must be contained in zero or one partitions;
|
||||||
|
\item set covering - all items must be contained in at least one partition.
|
||||||
|
\end{itemize}
|
||||||
|
In this case study a wedding planner must determine guest seating
|
||||||
|
allocations for
|
||||||
|
a wedding. To model this problem the tables are modelled as the partitions and the guests invited to the wedding
|
||||||
|
are modelled as the elements of S. The wedding planner wishes to maximise the total happiness of all of the tables.
|
||||||
|
|
||||||
|
A set partitioning problem may be modelled by explicitly enumerating each
|
||||||
|
possible subset. Though this approach does become intractable for large numbers of items (without using
|
||||||
|
column generation \cite{columngeneration}) it does have the advantage that the objective function co-efficients for
|
||||||
|
the partitions can be non-linear expressions (like happiness) and still allow this problem to be solved
|
||||||
|
using Linear Programming.
|
||||||
|
|
||||||
|
First we use \lstinline{pulp.allcombinations} to generate a list of all possible table seatings.
|
||||||
|
\lstinputlisting[linerange={20-22}]{code/wedding.py}
|
||||||
|
|
||||||
|
Then we create a binary variable that will be 1 if the table will be in the solution, or zero otherwise.
|
||||||
|
\lstinputlisting[linerange={24-28}]{code/wedding.py}
|
||||||
|
|
||||||
|
We create the \lstinline{LpProblem} and then make the objective function. Note that
|
||||||
|
happiness function used in this script would be difficult to model in any other way.
|
||||||
|
\lstinputlisting[linerange={30-32}]{code/wedding.py}
|
||||||
|
|
||||||
|
We specify the total number of tables allowed in the solution.
|
||||||
|
\lstinputlisting[linerange={34-35}]{code/wedding.py}
|
||||||
|
|
||||||
|
This set of constraints defines the set partitioning problem by guaranteeing that a guest is allocated to
|
||||||
|
exactly one table.
|
||||||
|
\lstinputlisting[linerange={38-41}]{code/wedding.py}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
\section{Why pulp}
|
||||||
|
Modelling problems using LP and solving them with pulp can be a very useful approach for the python programmer.
|
||||||
|
This approach allows the programmer to focus on the modelling rather than the algorithms that find the solution.
|
||||||
|
The third party LP solvers that pulp interfaces with are all mature pieces of software that can be trusted to
|
||||||
|
produce the correct solution to the model created in pulp. The difference between these solvers, for a user of pulp,
|
||||||
|
lies mainly in the trade off between cost of the solver and its speed to find a solution.
|
||||||
|
|
||||||
|
LP and IP are well researched areas of mathematics, therefore once a problem is stated as an LP model it is possible
|
||||||
|
to guarantee some properties of the solutions given. For instance, if the solver delivers an optimal solution it
|
||||||
|
will be impossible for someone to create a solution that is better (as measured by the objective function).
|
||||||
|
|
||||||
|
Compared to other LP modelling tools including AMPL \cite{ampl}, GAMS \cite{gams}, mathprog \cite{glpk}, flopc++ \cite{flopc}, and pyomo \cite{pyomo} and poams; pulp offers a number of advantages. These include its:
|
||||||
|
\begin{itemize}
|
||||||
|
\item non-restrictive licensing;
|
||||||
|
\item ease of installation;
|
||||||
|
\item clear syntax;
|
||||||
|
\item interoperability with an number of solvers;
|
||||||
|
\item extensive documentation.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\subsection{Licensing}
|
||||||
|
The licensing of pulp under a permissive open-source license allows pulp to be used as a medium for teaching
|
||||||
|
operations research as students can download and use pulp for free. The license allows OR practitioners to
|
||||||
|
integrate pulp in commercial applications for their clients without disclosing how the problem was solved.
|
||||||
|
The license also allows advanced users to modify and improve pulp for their own purposes.
|
||||||
|
|
||||||
|
\subsection{Installation}
|
||||||
|
Pulp is very easy to install. The provision of a setup.py file and registration on pipy, allows the user
|
||||||
|
to use
|
||||||
|
\begin{verbatim}
|
||||||
|
$easy_install pulp-or
|
||||||
|
\end{verbatim}
|
||||||
|
to download and install pulp on their system. For windows and ubuntu users this binary package also includes
|
||||||
|
the coin-or \cite{coin-or} solver so pulp will be immediately functional. For users on other platforms a compatible
|
||||||
|
solver must be installed for a pulp model to be solved.
|
||||||
|
|
||||||
|
\subsection{Syntax}
|
||||||
|
Ideally an LP modelling framework will allow a one-to-one translation of symbols from mathematical notation. If the syntax follows the formulation the programmer can ensure that the model written is the model required. The OR practitioner can also quickly read and understand the a model as written pulp, without having much python background. The standard mathematical notation is very concise and therefore code that mimics it will also be clear and concise.
|
||||||
|
|
||||||
|
The use of a `Pythonic' construction of the pulp framework allows the programmer to use it easily in a variety of ways. Pulp can be directly imported into the local name-space (against python style) to allow simple LP models to be written by non-programmers, in fact most of the case studies on the wiki \cite{pulpwiki} use this style. Pulp also does not force the programmer to use any particular way to store parameter data. Parameter data can be stored as lists, dictionaries or custom classes.
|
||||||
|
|
||||||
|
\subsection{Interoperability}
|
||||||
|
The ability for pulp to call a number of free and non-free solvers is important as the OR practitioner often wishes to compare the solution of a problem with a variety of solvers. The user may wish to develop simple LP models in a free solver and then provide a solution to a client using a commercial solver. The use of non-free solvers may dramatically reduce the solution time of the model. Pulp allows the free interchange of solvers without much change in the program, only a parameter for the \lstinline{LpProblem.solve} function is changed.
|
||||||
|
|
||||||
|
\subsection{Documentation}
|
||||||
|
Pulp is supplied with a user guide with extensive examples (in fact it was converted from a course teaching LP modelling). This user guide and the permissive license were intended to make pulp useful to the largest possible audience.
|
||||||
|
|
||||||
|
\section{Conclusion}
|
||||||
|
In conclusion pulp nicely bridges the gap between the OR practitioner and the python programmer. This allows the OR practitioner to use python to quickly develop and solve LP and IP models while having access to all of the tools available in the python standard library. The python programmer can now embed LP models in complex programs written in python. So next time you find yourself hacking up an algorithm to solve some problem in your code please consider if it would be appropriate to model this problem as an LP or IP instead. If so download and use pulp.
|
||||||
|
|
||||||
|
\section*{Appendix - Example Code in full}
|
||||||
|
\subsection*{whiskas.py}
|
||||||
|
\lstinputlisting{code/whiskas.py}
|
||||||
|
|
||||||
|
\subsection*{beerdistribution.py}
|
||||||
|
\lstinputlisting{code/beerdistribution.py}
|
||||||
|
|
||||||
|
\subsection*{wedding.py}
|
||||||
|
\lstinputlisting{code/wedding.py}
|
||||||
|
|
||||||
|
|
||||||
|
%-------------------------------------------------------------------------
|
||||||
|
%--- back matter: bibliography ---
|
||||||
|
% These are in alphabetical by leading author order.
|
||||||
|
\newpage
|
||||||
|
\bibliographystyle{IEEEtran}
|
||||||
|
\bibliography{references}
|
||||||
|
%-------------------------------------------------------------------------
|
||||||
|
% We need this for the style to work properly, so please leave it in here:
|
||||||
|
\label{lastpagenum}
|
||||||
|
\end{document}
|
||||||
898
doc/KPyCon2009/arlims.cls
Normal file
@@ -0,0 +1,898 @@
|
|||||||
|
%%
|
||||||
|
%% This is file `arlims.cls',
|
||||||
|
%% It is based on the original `article.cls' from a tetex 3.0
|
||||||
|
%% distribution (Ubuntu tetex-base 3.0-19 package).
|
||||||
|
%%
|
||||||
|
%% Additions to it were made by (probably) various authors at the
|
||||||
|
%% Massey University Institute of Information and Mathematical
|
||||||
|
%% Sciences.
|
||||||
|
%%
|
||||||
|
%% For further information contact Heath James
|
||||||
|
%% <h.a.james@massey.ac.nz>, Guy Kloss <g.kloss@massey.ac.nz>, Paul
|
||||||
|
%% Cowpertwait <p.s.cowpertwait@massey.ac.nz> or anybody you expect
|
||||||
|
%% suitable for this purpose ...
|
||||||
|
%%
|
||||||
|
%% Copyright 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004
|
||||||
|
%% The LaTeX3 Project and any individual authors listed elsewhere
|
||||||
|
%% in this file.
|
||||||
|
%%
|
||||||
|
%% This file was generated from file(s) of the LaTeX base system.
|
||||||
|
%% Numerous changes were made to be fit for RLIMS.
|
||||||
|
%% --------------------------------------------------------------
|
||||||
|
%%
|
||||||
|
%% Changes:
|
||||||
|
%%
|
||||||
|
%% * numerous changes previously to fit article.sty for the purpose
|
||||||
|
%%
|
||||||
|
%% version 1.5:
|
||||||
|
%%
|
||||||
|
%% * fixed the instructions
|
||||||
|
%% * added better default options to the template
|
||||||
|
%% * changed the year to 2008
|
||||||
|
%%
|
||||||
|
%% version 1.4:
|
||||||
|
%%
|
||||||
|
%% * fitted to a newer version of article.sty
|
||||||
|
%% * cleanups in layout and formatting
|
||||||
|
%% * better compliance to LaTeX2e
|
||||||
|
%% * re-fitted \institute, \instituteB, ...C, ...D
|
||||||
|
%% it doesn't work 100% nicely, but it works)
|
||||||
|
%% * removed dependency on natbib.sty (so cite.sty, or else can be
|
||||||
|
%% used as liked)
|
||||||
|
%% * changed paper size to a4paper by default
|
||||||
|
%% * removed dependencies to graphicx.sty and subfigure.sty
|
||||||
|
%% (users like to choose and do that themselves)
|
||||||
|
%%
|
||||||
|
%% --------------------------------------------------------------
|
||||||
|
%%
|
||||||
|
%% It may be distributed and/or modified under the
|
||||||
|
%% conditions of the LaTeX Project Public License, either version 1.3
|
||||||
|
%% of this license or (at your option) any later version.
|
||||||
|
%% The latest version of this license is in
|
||||||
|
%% http://www.latex-project.org/lppl.txt
|
||||||
|
%% and version 1.3 or later is part of all distributions of LaTeX
|
||||||
|
%% version 2003/12/01 or later.
|
||||||
|
%%
|
||||||
|
%% This file has the LPPL maintenance status "maintained".
|
||||||
|
%%
|
||||||
|
%% This file may only be distributed together with a copy of the LaTeX
|
||||||
|
%% base system. You may however distribute the LaTeX base system without
|
||||||
|
%% such generated files.
|
||||||
|
%%
|
||||||
|
%% The list of all files belonging to the LaTeX base distribution is
|
||||||
|
%% given in the file `manifest.txt'. See also `legal.txt' for additional
|
||||||
|
%% information.
|
||||||
|
%%
|
||||||
|
%% The list of derived (unpacked) files belonging to the distribution
|
||||||
|
%% and covered by LPPL is defined by the unpacking scripts (with
|
||||||
|
%% extension .ins) which are part of the distribution.
|
||||||
|
%% \CharacterTable
|
||||||
|
%% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z
|
||||||
|
%% Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z
|
||||||
|
%% Digits \0\1\2"s\4\5\6\7\8\9
|
||||||
|
%% Exclamation \! Double quote \" Hash (number) \#
|
||||||
|
%% Dollar \$ Percent \% Ampersand \&
|
||||||
|
%% Acute accent \' Left paren \( Right paren \)
|
||||||
|
%% Asterisk \* Plus \+ Comma \,
|
||||||
|
%% Minus \- Point \. Solidus \/
|
||||||
|
%% Colon \: Semicolon \; Less than \<
|
||||||
|
%% Equals \= Greater than \> Question mark \?
|
||||||
|
%% Commercial at \@ Left bracket \[ Backslash \\
|
||||||
|
%% Right bracket \] Circumflex \^ Underscore \_
|
||||||
|
%% Grave accent \` Left brace \{ Vertical bar \|
|
||||||
|
%% Right brace \} Tilde \~}
|
||||||
|
\NeedsTeXFormat{LaTeX2e}[1995/12/01]
|
||||||
|
\ProvidesClass{arlims}
|
||||||
|
[2008/04/23 v1.5
|
||||||
|
Massey Research Letters Article Class]
|
||||||
|
\newcommand\@ptsize{}
|
||||||
|
\newif\if@restonecol
|
||||||
|
\newif\if@titlepage
|
||||||
|
\@titlepagefalse
|
||||||
|
\if@compatibility\else
|
||||||
|
\DeclareOption{a4paper}
|
||||||
|
{\setlength\paperheight {297mm}%
|
||||||
|
\setlength\paperwidth {210mm}}
|
||||||
|
\DeclareOption{a5paper}
|
||||||
|
{\setlength\paperheight {210mm}%
|
||||||
|
\setlength\paperwidth {148mm}}
|
||||||
|
\DeclareOption{b5paper}
|
||||||
|
{\setlength\paperheight {250mm}%
|
||||||
|
\setlength\paperwidth {176mm}}
|
||||||
|
\DeclareOption{letterpaper}
|
||||||
|
{\setlength\paperheight {11in}%
|
||||||
|
\setlength\paperwidth {8.5in}}
|
||||||
|
\DeclareOption{legalpaper}
|
||||||
|
{\setlength\paperheight {14in}%
|
||||||
|
\setlength\paperwidth {8.5in}}
|
||||||
|
\DeclareOption{executivepaper}
|
||||||
|
{\setlength\paperheight {10.5in}%
|
||||||
|
\setlength\paperwidth {7.25in}}
|
||||||
|
\DeclareOption{landscape}
|
||||||
|
{\setlength\@tempdima {\paperheight}%
|
||||||
|
\setlength\paperheight {\paperwidth}%
|
||||||
|
\setlength\paperwidth {\@tempdima}}
|
||||||
|
\fi
|
||||||
|
\if@compatibility
|
||||||
|
\renewcommand\@ptsize{0}
|
||||||
|
\else
|
||||||
|
\DeclareOption{10pt}{\renewcommand\@ptsize{0}}
|
||||||
|
\fi
|
||||||
|
\DeclareOption{11pt}{\renewcommand\@ptsize{1}}
|
||||||
|
\DeclareOption{12pt}{\renewcommand\@ptsize{2}}
|
||||||
|
\if@compatibility\else
|
||||||
|
\DeclareOption{oneside}{\@twosidefalse \@mparswitchfalse}
|
||||||
|
\fi
|
||||||
|
\DeclareOption{twoside}{\@twosidetrue \@mparswitchtrue}
|
||||||
|
\DeclareOption{draft}{\setlength\overfullrule{5pt}}
|
||||||
|
\if@compatibility\else
|
||||||
|
\DeclareOption{final}{\setlength\overfullrule{0pt}}
|
||||||
|
\fi
|
||||||
|
\DeclareOption{titlepage}{\@titlepagetrue}
|
||||||
|
\if@compatibility\else
|
||||||
|
\DeclareOption{notitlepage}{\@titlepagefalse}
|
||||||
|
\fi
|
||||||
|
\if@compatibility\else
|
||||||
|
\DeclareOption{onecolumn}{\@twocolumnfalse}
|
||||||
|
\fi
|
||||||
|
\DeclareOption{twocolumn}{\@twocolumntrue}
|
||||||
|
\DeclareOption{leqno}{\input{leqno.clo}}
|
||||||
|
\DeclareOption{fleqn}{\input{fleqn.clo}}
|
||||||
|
\DeclareOption{openbib}{%
|
||||||
|
\AtEndOfPackage{%
|
||||||
|
\renewcommand\@openbib@code{%
|
||||||
|
\advance\leftmargin\bibindent
|
||||||
|
\itemindent -\bibindent
|
||||||
|
\listparindent \itemindent
|
||||||
|
\parsep \z@
|
||||||
|
}%
|
||||||
|
\renewcommand\newblock{\par}}%
|
||||||
|
}
|
||||||
|
\ExecuteOptions{a4paper,10pt,oneside,onecolumn,final}
|
||||||
|
\ProcessOptions
|
||||||
|
\input{size1\@ptsize.clo}
|
||||||
|
\setlength\lineskip{1\p@}
|
||||||
|
\setlength\normallineskip{1\p@}
|
||||||
|
\renewcommand\baselinestretch{}
|
||||||
|
\setlength\parskip{0\p@ \@plus \p@}
|
||||||
|
\@lowpenalty 51
|
||||||
|
\@medpenalty 151
|
||||||
|
\@highpenalty 301
|
||||||
|
\setcounter{topnumber}{2}
|
||||||
|
\renewcommand\topfraction{.7}
|
||||||
|
\setcounter{bottomnumber}{1}
|
||||||
|
\renewcommand\bottomfraction{.3}
|
||||||
|
\setcounter{totalnumber}{3}
|
||||||
|
\renewcommand\textfraction{.2}
|
||||||
|
\renewcommand\floatpagefraction{.5}
|
||||||
|
\setcounter{dbltopnumber}{2}
|
||||||
|
\renewcommand\dbltopfraction{.7}
|
||||||
|
\renewcommand\dblfloatpagefraction{.5}
|
||||||
|
\if@twoside
|
||||||
|
\def\ps@headings{%
|
||||||
|
\let\@oddfoot\@empty\let\@evenfoot\@empty
|
||||||
|
\def\@evenhead{\thepage\hfil\slshape\leftmark}%
|
||||||
|
\def\@oddhead{{\slshape\rightmark}\hfil\thepage}%
|
||||||
|
\let\@mkboth\markboth
|
||||||
|
\def\sectionmark##1{%
|
||||||
|
\markboth {\MakeUppercase{%
|
||||||
|
\ifnum \c@secnumdepth >\z@
|
||||||
|
\thesection\quad
|
||||||
|
\fi
|
||||||
|
##1}}{}}%
|
||||||
|
\def\subsectionmark##1{%
|
||||||
|
\markright {%
|
||||||
|
\ifnum \c@secnumdepth >\@ne
|
||||||
|
\thesubsection\quad
|
||||||
|
\fi
|
||||||
|
##1}}}
|
||||||
|
\else
|
||||||
|
\def\ps@headings{%
|
||||||
|
\let\@oddfoot\@empty
|
||||||
|
\def\@oddhead{{\slshape\rightmark}\hfil\thepage}%
|
||||||
|
\let\@mkboth\markboth
|
||||||
|
\def\sectionmark##1{%
|
||||||
|
\markright {\MakeUppercase{%
|
||||||
|
\ifnum \c@secnumdepth >\m@ne
|
||||||
|
\thesection\quad
|
||||||
|
\fi
|
||||||
|
##1}}}}
|
||||||
|
\fi
|
||||||
|
\def\ps@myheadings{%
|
||||||
|
\let\@oddfoot\@empty\let\@evenfoot\@empty
|
||||||
|
\def\@evenhead{\thepage\hfil\slshape\leftmark}%
|
||||||
|
\def\@oddhead{{\slshape\rightmark}\hfil\thepage}%
|
||||||
|
\let\@mkboth\@gobbletwo
|
||||||
|
\let\sectionmark\@gobble
|
||||||
|
\let\subsectionmark\@gobble
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
%---------- old definition of title page -------------------------------
|
||||||
|
\newif\ifGrxlb\ifGrxlb
|
||||||
|
|
||||||
|
\if@titlepage
|
||||||
|
\newcommand\maketitle{\begin{titlepage}%
|
||||||
|
\let\footnotesize\small
|
||||||
|
\let\footnoterule\relax
|
||||||
|
\let \footnote \thanks
|
||||||
|
\null\vfil
|
||||||
|
\vskip 60\p@
|
||||||
|
\begin{center}%
|
||||||
|
{\LARGE \@title \par}%
|
||||||
|
\vskip 3em%
|
||||||
|
{\large
|
||||||
|
\lineskip .75em%
|
||||||
|
\begin{tabular}[t]{c}%
|
||||||
|
\@author
|
||||||
|
\end{tabular}\par}%
|
||||||
|
\vskip 1.5em%
|
||||||
|
{\large \@date \par}% % Set date in \large size.
|
||||||
|
\end{center}\par
|
||||||
|
\@thanks
|
||||||
|
\vfil\null
|
||||||
|
\end{titlepage}%
|
||||||
|
\setcounter{footnote}{0}%
|
||||||
|
\global\let\thanks\relax
|
||||||
|
\global\let\maketitle\relax
|
||||||
|
\global\let\@thanks\@empty
|
||||||
|
\global\let\@author\@empty
|
||||||
|
\global\let\@date\@empty
|
||||||
|
\global\let\@title\@empty
|
||||||
|
\global\let\title\relax
|
||||||
|
\global\let\author\relax
|
||||||
|
\global\let\date\relax
|
||||||
|
\global\let\and\relax
|
||||||
|
}
|
||||||
|
\else
|
||||||
|
\newcommand\maketitle{\par
|
||||||
|
\begingroup
|
||||||
|
\renewcommand\thefootnote{\@fnsymbol\c@footnote}%
|
||||||
|
\def\@makefnmark{\rlap{\@textsuperscript{\normalfont\@thefnmark}}}%
|
||||||
|
\long\def\@makefntext##1{\parindent 1em\noindent
|
||||||
|
\hb@xt@1.8em{%
|
||||||
|
\hss\@textsuperscript{\normalfont\@thefnmark}}##1}%
|
||||||
|
\if@twocolumn
|
||||||
|
\ifnum \col@number=\@ne
|
||||||
|
\@maketitle
|
||||||
|
\else
|
||||||
|
\twocolumn[\@maketitle]%
|
||||||
|
\fi
|
||||||
|
\else
|
||||||
|
\newpage
|
||||||
|
\global\@topnum\z@ % Prevents figures from going at top of page.
|
||||||
|
\@maketitle
|
||||||
|
\fi
|
||||||
|
\thispagestyle{plain}\@thanks
|
||||||
|
\endgroup
|
||||||
|
\setcounter{footnote}{0}%
|
||||||
|
\global\let\thanks\relax
|
||||||
|
\global\let\maketitle\relax
|
||||||
|
\global\let\@maketitle\relax
|
||||||
|
\global\let\@thanks\@empty
|
||||||
|
\global\let\@author\@empty
|
||||||
|
\global\let\@date\@empty
|
||||||
|
\global\let\@title\@empty
|
||||||
|
\global\let\title\relax
|
||||||
|
\global\let\author\relax
|
||||||
|
\global\let\date\relax
|
||||||
|
\global\let\and\relax
|
||||||
|
}
|
||||||
|
\def\@maketitle{%
|
||||||
|
\newpage
|
||||||
|
\null
|
||||||
|
\vskip 2em%
|
||||||
|
\begin{center}%
|
||||||
|
\let \footnote \thanks
|
||||||
|
{\LARGE \@title \par}%
|
||||||
|
\vskip 1.5em%
|
||||||
|
{\large
|
||||||
|
\lineskip .5em%
|
||||||
|
\begin{tabular}[t]{c}%
|
||||||
|
\@author
|
||||||
|
\end{tabular}\par}%
|
||||||
|
\vskip 1em%
|
||||||
|
{\large \@date}%
|
||||||
|
\end{center}%
|
||||||
|
\par
|
||||||
|
\vskip 1.5em}
|
||||||
|
\fi
|
||||||
|
|
||||||
|
\fi
|
||||||
|
%---------------- end of old definition of title page ---------------------
|
||||||
|
|
||||||
|
\setcounter{secnumdepth}{3}
|
||||||
|
\newcounter {part}
|
||||||
|
\newcounter {section}
|
||||||
|
\newcounter {subsection}[section]
|
||||||
|
\newcounter {subsubsection}[subsection]
|
||||||
|
\newcounter {paragraph}[subsubsection]
|
||||||
|
\newcounter {subparagraph}[paragraph]
|
||||||
|
\renewcommand \thepart {\@Roman\c@part}
|
||||||
|
\renewcommand \thesection {\@arabic\c@section}
|
||||||
|
\renewcommand\thesubsection {\thesection.\@arabic\c@subsection}
|
||||||
|
\renewcommand\thesubsubsection{\thesubsection .\@arabic\c@subsubsection}
|
||||||
|
\renewcommand\theparagraph {\thesubsubsection.\@arabic\c@paragraph}
|
||||||
|
\renewcommand\thesubparagraph {\theparagraph.\@arabic\c@subparagraph}
|
||||||
|
\newcommand\part{%
|
||||||
|
\if@noskipsec \leavevmode \fi
|
||||||
|
\par
|
||||||
|
\addvspace{4ex}%
|
||||||
|
\@afterindentfalse
|
||||||
|
\secdef\@part\@spart}
|
||||||
|
|
||||||
|
\def\@part[#1]#2{%
|
||||||
|
\ifnum \c@secnumdepth >\m@ne
|
||||||
|
\refstepcounter{part}%
|
||||||
|
\addcontentsline{toc}{part}{\thepart\hspace{1em}#1}%
|
||||||
|
\else
|
||||||
|
\addcontentsline{toc}{part}{#1}%
|
||||||
|
\fi
|
||||||
|
{\parindent \z@ \raggedright
|
||||||
|
\interlinepenalty \@M
|
||||||
|
\normalfont
|
||||||
|
\ifnum \c@secnumdepth >\m@ne
|
||||||
|
\Large\bfseries \partname\nobreakspace\thepart
|
||||||
|
\par\nobreak
|
||||||
|
\fi
|
||||||
|
\huge \bfseries #2%
|
||||||
|
\markboth{}{}\par}%
|
||||||
|
\nobreak
|
||||||
|
\vskip 3ex
|
||||||
|
\@afterheading}
|
||||||
|
\def\@spart#1{%
|
||||||
|
{\parindent \z@ \raggedright
|
||||||
|
\interlinepenalty \@M
|
||||||
|
\normalfont
|
||||||
|
\huge \bfseries #1\par}%
|
||||||
|
\nobreak
|
||||||
|
\vskip 3ex
|
||||||
|
\@afterheading}
|
||||||
|
\newcommand\section{\@startsection {section}{1}{\z@}%
|
||||||
|
{-3.5ex \@plus -1ex \@minus -.2ex}%
|
||||||
|
{2.3ex \@plus.2ex}%
|
||||||
|
{\normalfont\Large\bfseries}}
|
||||||
|
\newcommand\subsection{\@startsection{subsection}{2}{\z@}%
|
||||||
|
{-3.25ex\@plus -1ex \@minus -.2ex}%
|
||||||
|
{1.5ex \@plus .2ex}%
|
||||||
|
{\normalfont\large\bfseries}}
|
||||||
|
\newcommand\subsubsection{\@startsection{subsubsection}{3}{\z@}%
|
||||||
|
{-3.25ex\@plus -1ex \@minus -.2ex}%
|
||||||
|
{1.5ex \@plus .2ex}%
|
||||||
|
{\normalfont\normalsize\bfseries}}
|
||||||
|
\newcommand\paragraph{\@startsection{paragraph}{4}{\z@}%
|
||||||
|
{3.25ex \@plus1ex \@minus.2ex}%
|
||||||
|
{-1em}%
|
||||||
|
{\normalfont\normalsize\bfseries}}
|
||||||
|
\newcommand\subparagraph{\@startsection{subparagraph}{5}{\parindent}%
|
||||||
|
{3.25ex \@plus1ex \@minus .2ex}%
|
||||||
|
{-1em}%
|
||||||
|
{\normalfont\normalsize\bfseries}}
|
||||||
|
\if@twocolumn
|
||||||
|
\setlength\leftmargini {2em}
|
||||||
|
\else
|
||||||
|
\setlength\leftmargini {2.5em}
|
||||||
|
\fi
|
||||||
|
\leftmargin \leftmargini
|
||||||
|
\setlength\leftmarginii {2.2em}
|
||||||
|
\setlength\leftmarginiii {1.87em}
|
||||||
|
\setlength\leftmarginiv {1.7em}
|
||||||
|
\if@twocolumn
|
||||||
|
\setlength\leftmarginv {.5em}
|
||||||
|
\setlength\leftmarginvi {.5em}
|
||||||
|
\else
|
||||||
|
\setlength\leftmarginv {1em}
|
||||||
|
\setlength\leftmarginvi {1em}
|
||||||
|
\fi
|
||||||
|
\setlength \labelsep {.5em}
|
||||||
|
\setlength \labelwidth{\leftmargini}
|
||||||
|
\addtolength\labelwidth{-\labelsep}
|
||||||
|
\@beginparpenalty -\@lowpenalty
|
||||||
|
\@endparpenalty -\@lowpenalty
|
||||||
|
\@itempenalty -\@lowpenalty
|
||||||
|
\renewcommand\theenumi{\@arabic\c@enumi}
|
||||||
|
\renewcommand\theenumii{\@alph\c@enumii}
|
||||||
|
\renewcommand\theenumiii{\@roman\c@enumiii}
|
||||||
|
\renewcommand\theenumiv{\@Alph\c@enumiv}
|
||||||
|
\newcommand\labelenumi{\theenumi.}
|
||||||
|
\newcommand\labelenumii{(\theenumii)}
|
||||||
|
\newcommand\labelenumiii{\theenumiii.}
|
||||||
|
\newcommand\labelenumiv{\theenumiv.}
|
||||||
|
\renewcommand\p@enumii{\theenumi}
|
||||||
|
\renewcommand\p@enumiii{\theenumi(\theenumii)}
|
||||||
|
\renewcommand\p@enumiv{\p@enumiii\theenumiii}
|
||||||
|
\newcommand\labelitemi{\textbullet}
|
||||||
|
\newcommand\labelitemii{\normalfont\bfseries \textendash}
|
||||||
|
\newcommand\labelitemiii{\textasteriskcentered}
|
||||||
|
\newcommand\labelitemiv{\textperiodcentered}
|
||||||
|
\newenvironment{description}
|
||||||
|
{\list{}{\labelwidth\z@ \itemindent-\leftmargin
|
||||||
|
\let\makelabel\descriptionlabel}}
|
||||||
|
{\endlist}
|
||||||
|
\newcommand*\descriptionlabel[1]{\hspace\labelsep
|
||||||
|
\normalfont\bfseries #1}
|
||||||
|
\if@titlepage
|
||||||
|
\newenvironment{abstract}{%
|
||||||
|
\titlepage
|
||||||
|
\null\vfil
|
||||||
|
\@beginparpenalty\@lowpenalty
|
||||||
|
\begin{center}%
|
||||||
|
\bfseries \abstractname
|
||||||
|
\@endparpenalty\@M
|
||||||
|
\end{center}}%
|
||||||
|
{\par\vfil\null\endtitlepage}
|
||||||
|
\else
|
||||||
|
\newenvironment{abstract}{%
|
||||||
|
\if@twocolumn
|
||||||
|
\section*{\abstractname}%
|
||||||
|
\else
|
||||||
|
\small
|
||||||
|
\begin{center}%
|
||||||
|
{\bfseries \abstractname\vspace{-.5em}\vspace{\z@}}%
|
||||||
|
\end{center}%
|
||||||
|
\quotation
|
||||||
|
\fi}
|
||||||
|
{\if@twocolumn\else\endquotation\fi}
|
||||||
|
\fi
|
||||||
|
\newenvironment{verse}
|
||||||
|
{\let\\\@centercr
|
||||||
|
\list{}{\itemsep \z@
|
||||||
|
\itemindent -1.5em%
|
||||||
|
\listparindent\itemindent
|
||||||
|
\rightmargin \leftmargin
|
||||||
|
\advance\leftmargin 1.5em}%
|
||||||
|
\item\relax}
|
||||||
|
{\endlist}
|
||||||
|
\newenvironment{quotation}
|
||||||
|
{\list{}{\listparindent 1.5em%
|
||||||
|
\itemindent \listparindent
|
||||||
|
\rightmargin \leftmargin
|
||||||
|
\parsep \z@ \@plus\p@}%
|
||||||
|
\item\relax}
|
||||||
|
{\endlist}
|
||||||
|
\newenvironment{quote}
|
||||||
|
{\list{}{\rightmargin\leftmargin}%
|
||||||
|
\item\relax}
|
||||||
|
{\endlist}
|
||||||
|
\if@compatibility
|
||||||
|
\newenvironment{titlepage}
|
||||||
|
{%
|
||||||
|
\if@twocolumn
|
||||||
|
\@restonecoltrue\onecolumn
|
||||||
|
\else
|
||||||
|
\@restonecolfalse\newpage
|
||||||
|
\fi
|
||||||
|
\thispagestyle{empty}%
|
||||||
|
\setcounter{page}\z@
|
||||||
|
}%
|
||||||
|
{\if@restonecol\twocolumn \else \newpage \fi
|
||||||
|
}
|
||||||
|
\else
|
||||||
|
\newenvironment{titlepage}
|
||||||
|
{%
|
||||||
|
\if@twocolumn
|
||||||
|
\@restonecoltrue\onecolumn
|
||||||
|
\else
|
||||||
|
\@restonecolfalse\newpage
|
||||||
|
\fi
|
||||||
|
\thispagestyle{empty}%
|
||||||
|
\setcounter{page}\@ne
|
||||||
|
}%
|
||||||
|
{\if@restonecol\twocolumn \else \newpage \fi
|
||||||
|
\if@twoside\else
|
||||||
|
\setcounter{page}\@ne
|
||||||
|
\fi
|
||||||
|
}
|
||||||
|
\fi
|
||||||
|
\newcommand\appendix{\par
|
||||||
|
\setcounter{section}{0}%
|
||||||
|
\setcounter{subsection}{0}%
|
||||||
|
\gdef\thesection{\@Alph\c@section}}
|
||||||
|
\setlength\arraycolsep{5\p@}
|
||||||
|
\setlength\tabcolsep{6\p@}
|
||||||
|
\setlength\arrayrulewidth{.4\p@}
|
||||||
|
\setlength\doublerulesep{2\p@}
|
||||||
|
\setlength\tabbingsep{\labelsep}
|
||||||
|
\skip\@mpfootins = \skip\footins
|
||||||
|
\setlength\fboxsep{3\p@}
|
||||||
|
\setlength\fboxrule{.4\p@}
|
||||||
|
\renewcommand \theequation {\@arabic\c@equation}
|
||||||
|
\newcounter{figure}
|
||||||
|
\renewcommand \thefigure {\@arabic\c@figure}
|
||||||
|
\def\fps@figure{tbp}
|
||||||
|
\def\ftype@figure{1}
|
||||||
|
\def\ext@figure{lof}
|
||||||
|
\def\fnum@figure{\figurename\nobreakspace\thefigure}
|
||||||
|
\newenvironment{figure}
|
||||||
|
{\@float{figure}}
|
||||||
|
{\end@float}
|
||||||
|
\newenvironment{figure*}
|
||||||
|
{\@dblfloat{figure}}
|
||||||
|
{\end@dblfloat}
|
||||||
|
\newcounter{table}
|
||||||
|
\renewcommand\thetable{\@arabic\c@table}
|
||||||
|
\def\fps@table{tbp}
|
||||||
|
\def\ftype@table{2}
|
||||||
|
\def\ext@table{lot}
|
||||||
|
\def\fnum@table{\tablename\nobreakspace\thetable}
|
||||||
|
\newenvironment{table}
|
||||||
|
{\@float{table}}
|
||||||
|
{\end@float}
|
||||||
|
\newenvironment{table*}
|
||||||
|
{\@dblfloat{table}}
|
||||||
|
{\end@dblfloat}
|
||||||
|
\newlength\abovecaptionskip
|
||||||
|
\newlength\belowcaptionskip
|
||||||
|
\setlength\abovecaptionskip{10\p@}
|
||||||
|
\setlength\belowcaptionskip{0\p@}
|
||||||
|
\long\def\@makecaption#1#2{%
|
||||||
|
\vskip\abovecaptionskip
|
||||||
|
\sbox\@tempboxa{#1: #2}%
|
||||||
|
\ifdim \wd\@tempboxa >\hsize
|
||||||
|
#1: #2\par
|
||||||
|
\else
|
||||||
|
\global \@minipagefalse
|
||||||
|
\hb@xt@\hsize{\hfil\box\@tempboxa\hfil}%
|
||||||
|
\fi
|
||||||
|
\vskip\belowcaptionskip}
|
||||||
|
\DeclareOldFontCommand{\rm}{\normalfont\rmfamily}{\mathrm}
|
||||||
|
\DeclareOldFontCommand{\sf}{\normalfont\sffamily}{\mathsf}
|
||||||
|
\DeclareOldFontCommand{\tt}{\normalfont\ttfamily}{\mathtt}
|
||||||
|
\DeclareOldFontCommand{\bf}{\normalfont\bfseries}{\mathbf}
|
||||||
|
\DeclareOldFontCommand{\it}{\normalfont\itshape}{\mathit}
|
||||||
|
\DeclareOldFontCommand{\sl}{\normalfont\slshape}{\@nomath\sl}
|
||||||
|
\DeclareOldFontCommand{\sc}{\normalfont\scshape}{\@nomath\sc}
|
||||||
|
\DeclareRobustCommand*\cal{\@fontswitch\relax\mathcal}
|
||||||
|
\DeclareRobustCommand*\mit{\@fontswitch\relax\mathnormal}
|
||||||
|
\newcommand\@pnumwidth{1.55em}
|
||||||
|
\newcommand\@tocrmarg{2.55em}
|
||||||
|
\newcommand\@dotsep{4.5}
|
||||||
|
\setcounter{tocdepth}{3}
|
||||||
|
\newcommand\tableofcontents{%
|
||||||
|
\section*{\contentsname
|
||||||
|
\@mkboth{%
|
||||||
|
\MakeUppercase\contentsname}{\MakeUppercase\contentsname}}%
|
||||||
|
\@starttoc{toc}%
|
||||||
|
}
|
||||||
|
\newcommand*\l@part[2]{%
|
||||||
|
\ifnum \c@tocdepth >-2\relax
|
||||||
|
\addpenalty\@secpenalty
|
||||||
|
\addvspace{2.25em \@plus\p@}%
|
||||||
|
\setlength\@tempdima{3em}%
|
||||||
|
\begingroup
|
||||||
|
\parindent \z@ \rightskip \@pnumwidth
|
||||||
|
\parfillskip -\@pnumwidth
|
||||||
|
{\leavevmode
|
||||||
|
\large \bfseries #1\hfil \hb@xt@\@pnumwidth{\hss #2}}\par
|
||||||
|
\nobreak
|
||||||
|
\if@compatibility
|
||||||
|
\global\@nobreaktrue
|
||||||
|
\everypar{\global\@nobreakfalse\everypar{}}%
|
||||||
|
\fi
|
||||||
|
\endgroup
|
||||||
|
\fi}
|
||||||
|
\newcommand*\l@section[2]{%
|
||||||
|
\ifnum \c@tocdepth >\z@
|
||||||
|
\addpenalty\@secpenalty
|
||||||
|
\addvspace{1.0em \@plus\p@}%
|
||||||
|
\setlength\@tempdima{1.5em}%
|
||||||
|
\begingroup
|
||||||
|
\parindent \z@ \rightskip \@pnumwidth
|
||||||
|
\parfillskip -\@pnumwidth
|
||||||
|
\leavevmode \bfseries
|
||||||
|
\advance\leftskip\@tempdima
|
||||||
|
\hskip -\leftskip
|
||||||
|
#1\nobreak\hfil \nobreak\hb@xt@\@pnumwidth{\hss #2}\par
|
||||||
|
\endgroup
|
||||||
|
\fi}
|
||||||
|
\newcommand*\l@subsection{\@dottedtocline{2}{1.5em}{2.3em}}
|
||||||
|
\newcommand*\l@subsubsection{\@dottedtocline{3}{3.8em}{3.2em}}
|
||||||
|
\newcommand*\l@paragraph{\@dottedtocline{4}{7.0em}{4.1em}}
|
||||||
|
\newcommand*\l@subparagraph{\@dottedtocline{5}{10em}{5em}}
|
||||||
|
\newcommand\listoffigures{%
|
||||||
|
\section*{\listfigurename}%
|
||||||
|
\@mkboth{\MakeUppercase\listfigurename}%
|
||||||
|
{\MakeUppercase\listfigurename}%
|
||||||
|
\@starttoc{lof}%
|
||||||
|
}
|
||||||
|
\newcommand*\l@figure{\@dottedtocline{1}{1.5em}{2.3em}}
|
||||||
|
\newcommand\listoftables{%
|
||||||
|
\section*{\listtablename}%
|
||||||
|
\@mkboth{%
|
||||||
|
\MakeUppercase\listtablename}%
|
||||||
|
{\MakeUppercase\listtablename}%
|
||||||
|
\@starttoc{lot}%
|
||||||
|
}
|
||||||
|
\let\l@table\l@figure
|
||||||
|
\newdimen\bibindent
|
||||||
|
\setlength\bibindent{1.5em}
|
||||||
|
\newenvironment{thebibliography}[1]
|
||||||
|
{\section*{\refname}%
|
||||||
|
\@mkboth{\MakeUppercase\refname}{\MakeUppercase\refname}%
|
||||||
|
\list{\@biblabel{\@arabic\c@enumiv}}%
|
||||||
|
{\settowidth\labelwidth{\@biblabel{#1}}%
|
||||||
|
\leftmargin\labelwidth
|
||||||
|
\advance\leftmargin\labelsep
|
||||||
|
\@openbib@code
|
||||||
|
\usecounter{enumiv}%
|
||||||
|
\let\p@enumiv\@empty
|
||||||
|
\renewcommand\theenumiv{\@arabic\c@enumiv}}%
|
||||||
|
\sloppy
|
||||||
|
\clubpenalty4000
|
||||||
|
\@clubpenalty \clubpenalty
|
||||||
|
\widowpenalty4000%
|
||||||
|
\sfcode`\.\@m}
|
||||||
|
{\def\@noitemerr
|
||||||
|
{\@latex@warning{Empty `thebibliography' environment}}%
|
||||||
|
\endlist}
|
||||||
|
\newcommand\newblock{\hskip .11em\@plus.33em\@minus.07em}
|
||||||
|
\let\@openbib@code\@empty
|
||||||
|
\newenvironment{theindex}
|
||||||
|
{\if@twocolumn
|
||||||
|
\@restonecolfalse
|
||||||
|
\else
|
||||||
|
\@restonecoltrue
|
||||||
|
\fi
|
||||||
|
\twocolumn[\section*{\indexname}]%
|
||||||
|
\@mkboth{\MakeUppercase\indexname}%
|
||||||
|
{\MakeUppercase\indexname}%
|
||||||
|
\thispagestyle{plain}\parindent\z@
|
||||||
|
\parskip\z@ \@plus .3\p@\relax
|
||||||
|
\columnseprule \z@
|
||||||
|
\columnsep 35\p@
|
||||||
|
\let\item\@idxitem}
|
||||||
|
{\if@restonecol\onecolumn\else\clearpage\fi}
|
||||||
|
\newcommand\@idxitem{\par\hangindent 40\p@}
|
||||||
|
\newcommand\subitem{\@idxitem \hspace*{20\p@}}
|
||||||
|
\newcommand\subsubitem{\@idxitem \hspace*{30\p@}}
|
||||||
|
\newcommand\indexspace{\par \vskip 10\p@ \@plus5\p@ \@minus3\p@\relax}
|
||||||
|
\renewcommand\footnoterule{%
|
||||||
|
\kern-3\p@
|
||||||
|
\hrule\@width.4\columnwidth
|
||||||
|
\kern2.6\p@}
|
||||||
|
\newcommand\@makefntext[1]{%
|
||||||
|
\parindent 1em%
|
||||||
|
\noindent
|
||||||
|
\hb@xt@1.8em{\hss\@makefnmark}#1}
|
||||||
|
\newcommand\contentsname{Contents}
|
||||||
|
\newcommand\listfigurename{List of Figures}
|
||||||
|
\newcommand\listtablename{List of Tables}
|
||||||
|
\newcommand\refname{References}
|
||||||
|
\newcommand\indexname{Index}
|
||||||
|
\newcommand\figurename{Figure}
|
||||||
|
\newcommand\tablename{Table}
|
||||||
|
\newcommand\partname{Part}
|
||||||
|
\newcommand\appendixname{Appendix}
|
||||||
|
\newcommand\abstractname{}
|
||||||
|
\def\today{\ifcase\month\or
|
||||||
|
January\or February\or March\or April\or May\or June\or
|
||||||
|
July\or August\or September\or October\or November\or December\fi
|
||||||
|
\space\number\day, \number\year}
|
||||||
|
\setlength\columnsep{10\p@}
|
||||||
|
\setlength\columnseprule{0\p@}
|
||||||
|
\pagestyle{plain}
|
||||||
|
\pagenumbering{arabic}
|
||||||
|
|
||||||
|
% === ARLIMS adaptions following ============================================
|
||||||
|
\pagestyle{empty}
|
||||||
|
|
||||||
|
\setlength{\oddsidemargin}{1.5 cm}
|
||||||
|
\setlength{\evensidemargin}{-0.5 cm}
|
||||||
|
\setlength{\textheight}{22 cm}
|
||||||
|
\setlength{\textwidth}{15 cm}
|
||||||
|
\setlength{\topmargin}{0 cm}
|
||||||
|
|
||||||
|
% --- special indexing to cope with the list of articles
|
||||||
|
\newif\ifIndexMade
|
||||||
|
\def\makeartIndex{%
|
||||||
|
\protected@write1{}{\string\global\string\IndexMadetrue}
|
||||||
|
\newwrite\@artIndexfile
|
||||||
|
\immediate\openout\@artIndexfile=\jobname.adx
|
||||||
|
\def\artIndex{\@bsphack\begingroup\@sanitize\@wrartIndex}
|
||||||
|
\typeout{Writing artIndex file \jobname.adx}%
|
||||||
|
\let\makeartIndex\@empty
|
||||||
|
}
|
||||||
|
|
||||||
|
\def\@wrartIndex#1{%
|
||||||
|
\protected@write\@artIndexfile{}{%
|
||||||
|
\string\artIndexentry{\factTitle}%
|
||||||
|
{\thepage}%
|
||||||
|
{\string\last#1}%
|
||||||
|
{\factAuthor\factAuthorB\factAuthorC\factAuthorD}}%
|
||||||
|
\endgroup%
|
||||||
|
\@esphack%
|
||||||
|
}
|
||||||
|
|
||||||
|
\def\artIndex{\@bsphack\begingroup \@sanitize\@artIndex}
|
||||||
|
\def\@artIndex#1{\endgroup\@esphack}
|
||||||
|
|
||||||
|
\def\artIndexLast#1{%
|
||||||
|
\protected@write1{}{\string\gdef\string\last#1{\thepage}}%
|
||||||
|
}
|
||||||
|
|
||||||
|
% --- to set up the headers permanently
|
||||||
|
\newcommand{\markperm}[2]{%
|
||||||
|
\global\def\@evenhead{\arabic{page}\hfil{#1}}%
|
||||||
|
\global\def\@oddhead{{#2}\hfil\arabic{page}}
|
||||||
|
}
|
||||||
|
% (yes, I know I've hardwired arabic page numbering)
|
||||||
|
|
||||||
|
% --- to set up the headers with a once off first
|
||||||
|
\newcommand{\markonce}[3]{%
|
||||||
|
\global\def\@evenhead{\arabic{page}\hfil{#1}\markperm{#2}{#3}}%
|
||||||
|
\global\def\@oddhead{{#1}\hfil\arabic{page}\markperm{#2}{#3}}%
|
||||||
|
}
|
||||||
|
|
||||||
|
%% Put a line after the abstract %%
|
||||||
|
\newcommand{\drawline}{%
|
||||||
|
\vspace*{1mm}
|
||||||
|
\begin{center}
|
||||||
|
\rule{\textwidth}{0.2mm}
|
||||||
|
\end{center}
|
||||||
|
\vspace*{1mm}
|
||||||
|
}
|
||||||
|
|
||||||
|
\newcommand{\reff}[1]{(\ref{#1})}
|
||||||
|
|
||||||
|
|
||||||
|
% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
% Define the author and title commands. Bascially each just stuffs
|
||||||
|
% the argument into a storage command. I need to define the blank
|
||||||
|
% storage commands because I am using the "renewcomand", since I
|
||||||
|
% need to do this for each article.
|
||||||
|
|
||||||
|
\def\author{}
|
||||||
|
\def\institute{}
|
||||||
|
|
||||||
|
\def\factTitle{}
|
||||||
|
|
||||||
|
\def\factAuthor{}
|
||||||
|
\def\factAuthorB{}
|
||||||
|
\def\factAuthorC{}
|
||||||
|
\def\factAuthorD{}
|
||||||
|
|
||||||
|
\def\factInstitute{}
|
||||||
|
\def\factInstituteB{}
|
||||||
|
\def\factInstituteC{}
|
||||||
|
\def\factInstituteD{}
|
||||||
|
|
||||||
|
\def\footInst{}
|
||||||
|
\def\footInstB{}
|
||||||
|
\def\footInstC{}
|
||||||
|
\def\footInstD{}
|
||||||
|
|
||||||
|
\def\footAuthorInst{}
|
||||||
|
\def\footAuthorInstB{}
|
||||||
|
\def\footAuthorInstC{}
|
||||||
|
\def\footAuthorInstD{}
|
||||||
|
|
||||||
|
\renewcommand{\author}[2][]{
|
||||||
|
\renewcommand{\footAuthorInst}{#1}
|
||||||
|
\renewcommand{\factAuthor}{#2}
|
||||||
|
}
|
||||||
|
|
||||||
|
\newcommand{\authorB}[2][]{
|
||||||
|
\renewcommand{\footAuthorInstB}{#1}
|
||||||
|
\renewcommand{\factAuthorB}{#2}
|
||||||
|
}
|
||||||
|
|
||||||
|
\newcommand{\authorC}[2][]{
|
||||||
|
\renewcommand{\footAuthorInstC}{#1}
|
||||||
|
\renewcommand{\factAuthorC}{#2}
|
||||||
|
}
|
||||||
|
|
||||||
|
\newcommand{\authorD}[2][]{
|
||||||
|
\renewcommand{\footAuthorInstD}{#1}
|
||||||
|
\renewcommand{\factAuthorD}{#2}
|
||||||
|
}
|
||||||
|
|
||||||
|
\renewcommand{\institute}[2][]{
|
||||||
|
\renewcommand{\footInst}{#1}
|
||||||
|
\renewcommand{\factInstitute}{#2}
|
||||||
|
}
|
||||||
|
|
||||||
|
\newcommand{\instituteB}[2][*]{
|
||||||
|
\renewcommand{\footInstB}{#1}
|
||||||
|
\renewcommand{\factInstituteB}{#2}
|
||||||
|
}
|
||||||
|
|
||||||
|
\newcommand{\instituteC}[2][**]{
|
||||||
|
\renewcommand{\footInstC}{#1}
|
||||||
|
\renewcommand{\factInstituteC}{#2}
|
||||||
|
}
|
||||||
|
|
||||||
|
\newcommand{\instituteD}[2][***]{
|
||||||
|
\renewcommand{\footInstD}{#1}
|
||||||
|
\renewcommand{\factInstituteD}{#2}
|
||||||
|
}
|
||||||
|
|
||||||
|
\def\nameTag{}
|
||||||
|
|
||||||
|
\renewcommand{\title}[1]{
|
||||||
|
\renewcommand{\factTitle}{#1}
|
||||||
|
}
|
||||||
|
|
||||||
|
\newcommand{\iimsaddress}{
|
||||||
|
\begin{center}
|
||||||
|
\textit{Institute of Information \& Mathematical Sciences\\
|
||||||
|
Massey University at Albany, Auckland, New Zealand.}
|
||||||
|
\end{center}
|
||||||
|
}
|
||||||
|
|
||||||
|
\newcommand{\maketitle}[1][\factTitle]{
|
||||||
|
\begin{center}
|
||||||
|
\markonce{
|
||||||
|
\parbox{10cm}{\small
|
||||||
|
\textit{Res.\ Lett.\ Inf.\ Math.\ Sci.}, 2009, Vol.~\textbf{13}, pp.~\arabic{page}--\pageref{lastpagenum} \\[1mm]
|
||||||
|
Available online at http://iims.massey.ac.nz/research/letters/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{\factAuthor\factAuthorB\factAuthorC\factAuthorD}
|
||||||
|
{#1}
|
||||||
|
|
||||||
|
\vspace*{1cm}
|
||||||
|
|
||||||
|
\begin{minipage}{12cm}
|
||||||
|
\begin{center}
|
||||||
|
{\bfseries\Large\factTitle\par}
|
||||||
|
\vspace*{2mm}
|
||||||
|
\end{center}
|
||||||
|
\end{minipage}
|
||||||
|
|
||||||
|
\vspace*{3mm}
|
||||||
|
|
||||||
|
\textsc{%
|
||||||
|
\factAuthor $^{\footAuthorInst}$%
|
||||||
|
\factAuthorB $^{\footAuthorInstB}$%
|
||||||
|
\factAuthorC $^{\footAuthorInstC}$%
|
||||||
|
\factAuthorD $^{\footAuthorInstD}$%
|
||||||
|
}
|
||||||
|
|
||||||
|
\vspace*{1mm}
|
||||||
|
|
||||||
|
$^{\footInst}$\textit{\factInstitute}
|
||||||
|
$^{\footInstB}$\textit{\factInstituteB}
|
||||||
|
$^{\footInstC}$\textit{\factInstituteC}
|
||||||
|
$^{\footInstD}$\textit{\factInstituteD}
|
||||||
|
|
||||||
|
\artIndex{\nameTag}
|
||||||
|
\end{center}
|
||||||
|
}
|
||||||
|
|
||||||
|
% calculate the margins so that the text on opposite sides
|
||||||
|
% of the same sheet is lined up at the edges
|
||||||
|
% ok, its a fudge, but you can change the oddsidemargin
|
||||||
|
% to shift both sides in sync.
|
||||||
|
|
||||||
|
\oddsidemargin1cm
|
||||||
|
\evensidemargin\paperwidth
|
||||||
|
\addtolength{\evensidemargin}{-\oddsidemargin}
|
||||||
|
\addtolength{\evensidemargin}{-205mm}
|
||||||
|
|
||||||
|
% --- turn off a few things -------------------------------------------------
|
||||||
|
\renewcommand{\markboth}[2]{}
|
||||||
|
\renewcommand{\markright}[1]{}
|
||||||
|
\newcommand{\markheading}[1]{}
|
||||||
|
|
||||||
|
\usepackage{amsmath,amsfonts,dsfont}
|
||||||
|
\usepackage{amsthm}
|
||||||
|
|
||||||
|
\newif\ifCombinedRlims
|
||||||
|
|
||||||
|
% === end of ARLIMS adaptions ===============================================
|
||||||
|
|
||||||
|
\if@twoside
|
||||||
|
\else
|
||||||
|
\raggedbottom
|
||||||
|
\fi
|
||||||
|
\if@twocolumn
|
||||||
|
\twocolumn
|
||||||
|
\sloppy
|
||||||
|
\flushbottom
|
||||||
|
\else
|
||||||
|
\onecolumn
|
||||||
|
\fi
|
||||||
|
\endinput
|
||||||
|
%%
|
||||||
|
%% End of file `arlims.cls'.
|
||||||
898
doc/KPyCon2009/arlimsTPP.cls
Normal file
@@ -0,0 +1,898 @@
|
|||||||
|
%%
|
||||||
|
%% This is file `arlims.cls',
|
||||||
|
%% It is based on the original `article.cls' from a tetex 3.0
|
||||||
|
%% distribution (Ubuntu tetex-base 3.0-19 package).
|
||||||
|
%%
|
||||||
|
%% Additions to it were made by (probably) various authors at the
|
||||||
|
%% Massey University Institute of Information and Mathematical
|
||||||
|
%% Sciences.
|
||||||
|
%%
|
||||||
|
%% For further information contact Heath James
|
||||||
|
%% <h.a.james@massey.ac.nz>, Guy Kloss <g.kloss@massey.ac.nz>, Paul
|
||||||
|
%% Cowpertwait <p.s.cowpertwait@massey.ac.nz> or anybody you expect
|
||||||
|
%% suitable for this purpose ...
|
||||||
|
%%
|
||||||
|
%% Copyright 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004
|
||||||
|
%% The LaTeX3 Project and any individual authors listed elsewhere
|
||||||
|
%% in this file.
|
||||||
|
%%
|
||||||
|
%% This file was generated from file(s) of the LaTeX base system.
|
||||||
|
%% Numerous changes were made to be fit for RLIMS.
|
||||||
|
%% --------------------------------------------------------------
|
||||||
|
%%
|
||||||
|
%% Changes:
|
||||||
|
%%
|
||||||
|
%% * numerous changes previously to fit article.sty for the purpose
|
||||||
|
%%
|
||||||
|
%% version 1.5:
|
||||||
|
%%
|
||||||
|
%% * fixed the instructions
|
||||||
|
%% * added better default options to the template
|
||||||
|
%% * changed the year to 2008
|
||||||
|
%%
|
||||||
|
%% version 1.4:
|
||||||
|
%%
|
||||||
|
%% * fitted to a newer version of article.sty
|
||||||
|
%% * cleanups in layout and formatting
|
||||||
|
%% * better compliance to LaTeX2e
|
||||||
|
%% * re-fitted \institute, \instituteB, ...C, ...D
|
||||||
|
%% it doesn't work 100% nicely, but it works)
|
||||||
|
%% * removed dependency on natbib.sty (so cite.sty, or else can be
|
||||||
|
%% used as liked)
|
||||||
|
%% * changed paper size to a4paper by default
|
||||||
|
%% * removed dependencies to graphicx.sty and subfigure.sty
|
||||||
|
%% (users like to choose and do that themselves)
|
||||||
|
%%
|
||||||
|
%% --------------------------------------------------------------
|
||||||
|
%%
|
||||||
|
%% It may be distributed and/or modified under the
|
||||||
|
%% conditions of the LaTeX Project Public License, either version 1.3
|
||||||
|
%% of this license or (at your option) any later version.
|
||||||
|
%% The latest version of this license is in
|
||||||
|
%% http://www.latex-project.org/lppl.txt
|
||||||
|
%% and version 1.3 or later is part of all distributions of LaTeX
|
||||||
|
%% version 2003/12/01 or later.
|
||||||
|
%%
|
||||||
|
%% This file has the LPPL maintenance status "maintained".
|
||||||
|
%%
|
||||||
|
%% This file may only be distributed together with a copy of the LaTeX
|
||||||
|
%% base system. You may however distribute the LaTeX base system without
|
||||||
|
%% such generated files.
|
||||||
|
%%
|
||||||
|
%% The list of all files belonging to the LaTeX base distribution is
|
||||||
|
%% given in the file `manifest.txt'. See also `legal.txt' for additional
|
||||||
|
%% information.
|
||||||
|
%%
|
||||||
|
%% The list of derived (unpacked) files belonging to the distribution
|
||||||
|
%% and covered by LPPL is defined by the unpacking scripts (with
|
||||||
|
%% extension .ins) which are part of the distribution.
|
||||||
|
%% \CharacterTable
|
||||||
|
%% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z
|
||||||
|
%% Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z
|
||||||
|
%% Digits \0\1\2"s\4\5\6\7\8\9
|
||||||
|
%% Exclamation \! Double quote \" Hash (number) \#
|
||||||
|
%% Dollar \$ Percent \% Ampersand \&
|
||||||
|
%% Acute accent \' Left paren \( Right paren \)
|
||||||
|
%% Asterisk \* Plus \+ Comma \,
|
||||||
|
%% Minus \- Point \. Solidus \/
|
||||||
|
%% Colon \: Semicolon \; Less than \<
|
||||||
|
%% Equals \= Greater than \> Question mark \?
|
||||||
|
%% Commercial at \@ Left bracket \[ Backslash \\
|
||||||
|
%% Right bracket \] Circumflex \^ Underscore \_
|
||||||
|
%% Grave accent \` Left brace \{ Vertical bar \|
|
||||||
|
%% Right brace \} Tilde \~}
|
||||||
|
\NeedsTeXFormat{LaTeX2e}[1995/12/01]
|
||||||
|
\ProvidesClass{arlims}
|
||||||
|
[2008/04/23 v1.5
|
||||||
|
Massey Research Letters Article Class]
|
||||||
|
\newcommand\@ptsize{}
|
||||||
|
\newif\if@restonecol
|
||||||
|
\newif\if@titlepage
|
||||||
|
\@titlepagefalse
|
||||||
|
\if@compatibility\else
|
||||||
|
\DeclareOption{a4paper}
|
||||||
|
{\setlength\paperheight {297mm}%
|
||||||
|
\setlength\paperwidth {210mm}}
|
||||||
|
\DeclareOption{a5paper}
|
||||||
|
{\setlength\paperheight {210mm}%
|
||||||
|
\setlength\paperwidth {148mm}}
|
||||||
|
\DeclareOption{b5paper}
|
||||||
|
{\setlength\paperheight {250mm}%
|
||||||
|
\setlength\paperwidth {176mm}}
|
||||||
|
\DeclareOption{letterpaper}
|
||||||
|
{\setlength\paperheight {11in}%
|
||||||
|
\setlength\paperwidth {8.5in}}
|
||||||
|
\DeclareOption{legalpaper}
|
||||||
|
{\setlength\paperheight {14in}%
|
||||||
|
\setlength\paperwidth {8.5in}}
|
||||||
|
\DeclareOption{executivepaper}
|
||||||
|
{\setlength\paperheight {10.5in}%
|
||||||
|
\setlength\paperwidth {7.25in}}
|
||||||
|
\DeclareOption{landscape}
|
||||||
|
{\setlength\@tempdima {\paperheight}%
|
||||||
|
\setlength\paperheight {\paperwidth}%
|
||||||
|
\setlength\paperwidth {\@tempdima}}
|
||||||
|
\fi
|
||||||
|
\if@compatibility
|
||||||
|
\renewcommand\@ptsize{0}
|
||||||
|
\else
|
||||||
|
\DeclareOption{10pt}{\renewcommand\@ptsize{0}}
|
||||||
|
\fi
|
||||||
|
\DeclareOption{11pt}{\renewcommand\@ptsize{1}}
|
||||||
|
\DeclareOption{12pt}{\renewcommand\@ptsize{2}}
|
||||||
|
\if@compatibility\else
|
||||||
|
\DeclareOption{oneside}{\@twosidefalse \@mparswitchfalse}
|
||||||
|
\fi
|
||||||
|
\DeclareOption{twoside}{\@twosidetrue \@mparswitchtrue}
|
||||||
|
\DeclareOption{draft}{\setlength\overfullrule{5pt}}
|
||||||
|
\if@compatibility\else
|
||||||
|
\DeclareOption{final}{\setlength\overfullrule{0pt}}
|
||||||
|
\fi
|
||||||
|
\DeclareOption{titlepage}{\@titlepagetrue}
|
||||||
|
\if@compatibility\else
|
||||||
|
\DeclareOption{notitlepage}{\@titlepagefalse}
|
||||||
|
\fi
|
||||||
|
\if@compatibility\else
|
||||||
|
\DeclareOption{onecolumn}{\@twocolumnfalse}
|
||||||
|
\fi
|
||||||
|
\DeclareOption{twocolumn}{\@twocolumntrue}
|
||||||
|
\DeclareOption{leqno}{\input{leqno.clo}}
|
||||||
|
\DeclareOption{fleqn}{\input{fleqn.clo}}
|
||||||
|
\DeclareOption{openbib}{%
|
||||||
|
\AtEndOfPackage{%
|
||||||
|
\renewcommand\@openbib@code{%
|
||||||
|
\advance\leftmargin\bibindent
|
||||||
|
\itemindent -\bibindent
|
||||||
|
\listparindent \itemindent
|
||||||
|
\parsep \z@
|
||||||
|
}%
|
||||||
|
\renewcommand\newblock{\par}}%
|
||||||
|
}
|
||||||
|
\ExecuteOptions{a4paper,12pt,oneside,onecolumn,final}
|
||||||
|
\ProcessOptions
|
||||||
|
\input{size1\@ptsize.clo}
|
||||||
|
\setlength\lineskip{1\p@}
|
||||||
|
\setlength\normallineskip{1\p@}
|
||||||
|
\renewcommand\baselinestretch{}
|
||||||
|
\setlength\parskip{0\p@ \@plus \p@}
|
||||||
|
\@lowpenalty 51
|
||||||
|
\@medpenalty 151
|
||||||
|
\@highpenalty 301
|
||||||
|
\setcounter{topnumber}{2}
|
||||||
|
\renewcommand\topfraction{.7}
|
||||||
|
\setcounter{bottomnumber}{1}
|
||||||
|
\renewcommand\bottomfraction{.3}
|
||||||
|
\setcounter{totalnumber}{3}
|
||||||
|
\renewcommand\textfraction{.2}
|
||||||
|
\renewcommand\floatpagefraction{.5}
|
||||||
|
\setcounter{dbltopnumber}{2}
|
||||||
|
\renewcommand\dbltopfraction{.7}
|
||||||
|
\renewcommand\dblfloatpagefraction{.5}
|
||||||
|
\if@twoside
|
||||||
|
\def\ps@headings{%
|
||||||
|
\let\@oddfoot\@empty\let\@evenfoot\@empty
|
||||||
|
\def\@evenhead{\thepage\hfil\slshape\leftmark}%
|
||||||
|
\def\@oddhead{{\slshape\rightmark}\hfil\thepage}%
|
||||||
|
\let\@mkboth\markboth
|
||||||
|
\def\sectionmark##1{%
|
||||||
|
\markboth {\MakeUppercase{%
|
||||||
|
\ifnum \c@secnumdepth >\z@
|
||||||
|
\thesection\quad
|
||||||
|
\fi
|
||||||
|
##1}}{}}%
|
||||||
|
\def\subsectionmark##1{%
|
||||||
|
\markright {%
|
||||||
|
\ifnum \c@secnumdepth >\@ne
|
||||||
|
\thesubsection\quad
|
||||||
|
\fi
|
||||||
|
##1}}}
|
||||||
|
\else
|
||||||
|
\def\ps@headings{%
|
||||||
|
\let\@oddfoot\@empty
|
||||||
|
\def\@oddhead{{\slshape\rightmark}\hfil\thepage}%
|
||||||
|
\let\@mkboth\markboth
|
||||||
|
\def\sectionmark##1{%
|
||||||
|
\markright {\MakeUppercase{%
|
||||||
|
\ifnum \c@secnumdepth >\m@ne
|
||||||
|
\thesection\quad
|
||||||
|
\fi
|
||||||
|
##1}}}}
|
||||||
|
\fi
|
||||||
|
\def\ps@myheadings{%
|
||||||
|
\let\@oddfoot\@empty\let\@evenfoot\@empty
|
||||||
|
\def\@evenhead{\thepage\hfil\slshape\leftmark}%
|
||||||
|
\def\@oddhead{{\slshape\rightmark}\hfil\thepage}%
|
||||||
|
\let\@mkboth\@gobbletwo
|
||||||
|
\let\sectionmark\@gobble
|
||||||
|
\let\subsectionmark\@gobble
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
%---------- old definition of title page -------------------------------
|
||||||
|
\newif\ifGrxlb\ifGrxlb
|
||||||
|
|
||||||
|
\if@titlepage
|
||||||
|
\newcommand\maketitle{\begin{titlepage}%
|
||||||
|
\let\footnotesize\small
|
||||||
|
\let\footnoterule\relax
|
||||||
|
\let \footnote \thanks
|
||||||
|
\null\vfil
|
||||||
|
\vskip 60\p@
|
||||||
|
\begin{center}%
|
||||||
|
{\LARGE \@title \par}%
|
||||||
|
\vskip 3em%
|
||||||
|
{\large
|
||||||
|
\lineskip .75em%
|
||||||
|
\begin{tabular}[t]{c}%
|
||||||
|
\@author
|
||||||
|
\end{tabular}\par}%
|
||||||
|
\vskip 1.5em%
|
||||||
|
{\large \@date \par}% % Set date in \large size.
|
||||||
|
\end{center}\par
|
||||||
|
\@thanks
|
||||||
|
\vfil\null
|
||||||
|
\end{titlepage}%
|
||||||
|
\setcounter{footnote}{0}%
|
||||||
|
\global\let\thanks\relax
|
||||||
|
\global\let\maketitle\relax
|
||||||
|
\global\let\@thanks\@empty
|
||||||
|
\global\let\@author\@empty
|
||||||
|
\global\let\@date\@empty
|
||||||
|
\global\let\@title\@empty
|
||||||
|
\global\let\title\relax
|
||||||
|
\global\let\author\relax
|
||||||
|
\global\let\date\relax
|
||||||
|
\global\let\and\relax
|
||||||
|
}
|
||||||
|
\else
|
||||||
|
\newcommand\maketitle{\par
|
||||||
|
\begingroup
|
||||||
|
\renewcommand\thefootnote{\@fnsymbol\c@footnote}%
|
||||||
|
\def\@makefnmark{\rlap{\@textsuperscript{\normalfont\@thefnmark}}}%
|
||||||
|
\long\def\@makefntext##1{\parindent 1em\noindent
|
||||||
|
\hb@xt@1.8em{%
|
||||||
|
\hss\@textsuperscript{\normalfont\@thefnmark}}##1}%
|
||||||
|
\if@twocolumn
|
||||||
|
\ifnum \col@number=\@ne
|
||||||
|
\@maketitle
|
||||||
|
\else
|
||||||
|
\twocolumn[\@maketitle]%
|
||||||
|
\fi
|
||||||
|
\else
|
||||||
|
\newpage
|
||||||
|
\global\@topnum\z@ % Prevents figures from going at top of page.
|
||||||
|
\@maketitle
|
||||||
|
\fi
|
||||||
|
\thispagestyle{plain}\@thanks
|
||||||
|
\endgroup
|
||||||
|
\setcounter{footnote}{0}%
|
||||||
|
\global\let\thanks\relax
|
||||||
|
\global\let\maketitle\relax
|
||||||
|
\global\let\@maketitle\relax
|
||||||
|
\global\let\@thanks\@empty
|
||||||
|
\global\let\@author\@empty
|
||||||
|
\global\let\@date\@empty
|
||||||
|
\global\let\@title\@empty
|
||||||
|
\global\let\title\relax
|
||||||
|
\global\let\author\relax
|
||||||
|
\global\let\date\relax
|
||||||
|
\global\let\and\relax
|
||||||
|
}
|
||||||
|
\def\@maketitle{%
|
||||||
|
\newpage
|
||||||
|
\null
|
||||||
|
\vskip 2em%
|
||||||
|
\begin{center}%
|
||||||
|
\let \footnote \thanks
|
||||||
|
{\LARGE \@title \par}%
|
||||||
|
\vskip 1.5em%
|
||||||
|
{\large
|
||||||
|
\lineskip .5em%
|
||||||
|
\begin{tabular}[t]{c}%
|
||||||
|
\@author
|
||||||
|
\end{tabular}\par}%
|
||||||
|
\vskip 1em%
|
||||||
|
{\large \@date}%
|
||||||
|
\end{center}%
|
||||||
|
\par
|
||||||
|
\vskip 1.5em}
|
||||||
|
\fi
|
||||||
|
|
||||||
|
\fi
|
||||||
|
%---------------- end of old definition of title page ---------------------
|
||||||
|
|
||||||
|
\setcounter{secnumdepth}{3}
|
||||||
|
\newcounter {part}
|
||||||
|
\newcounter {section}
|
||||||
|
\newcounter {subsection}[section]
|
||||||
|
\newcounter {subsubsection}[subsection]
|
||||||
|
\newcounter {paragraph}[subsubsection]
|
||||||
|
\newcounter {subparagraph}[paragraph]
|
||||||
|
\renewcommand \thepart {\@Roman\c@part}
|
||||||
|
\renewcommand \thesection {\@arabic\c@section}
|
||||||
|
\renewcommand\thesubsection {\thesection.\@arabic\c@subsection}
|
||||||
|
\renewcommand\thesubsubsection{\thesubsection .\@arabic\c@subsubsection}
|
||||||
|
\renewcommand\theparagraph {\thesubsubsection.\@arabic\c@paragraph}
|
||||||
|
\renewcommand\thesubparagraph {\theparagraph.\@arabic\c@subparagraph}
|
||||||
|
\newcommand\part{%
|
||||||
|
\if@noskipsec \leavevmode \fi
|
||||||
|
\par
|
||||||
|
\addvspace{4ex}%
|
||||||
|
\@afterindentfalse
|
||||||
|
\secdef\@part\@spart}
|
||||||
|
|
||||||
|
\def\@part[#1]#2{%
|
||||||
|
\ifnum \c@secnumdepth >\m@ne
|
||||||
|
\refstepcounter{part}%
|
||||||
|
\addcontentsline{toc}{part}{\thepart\hspace{1em}#1}%
|
||||||
|
\else
|
||||||
|
\addcontentsline{toc}{part}{#1}%
|
||||||
|
\fi
|
||||||
|
{\parindent \z@ \raggedright
|
||||||
|
\interlinepenalty \@M
|
||||||
|
\normalfont
|
||||||
|
\ifnum \c@secnumdepth >\m@ne
|
||||||
|
\Large\bfseries \partname\nobreakspace\thepart
|
||||||
|
\par\nobreak
|
||||||
|
\fi
|
||||||
|
\huge \bfseries #2%
|
||||||
|
\markboth{}{}\par}%
|
||||||
|
\nobreak
|
||||||
|
\vskip 3ex
|
||||||
|
\@afterheading}
|
||||||
|
\def\@spart#1{%
|
||||||
|
{\parindent \z@ \raggedright
|
||||||
|
\interlinepenalty \@M
|
||||||
|
\normalfont
|
||||||
|
\huge \bfseries #1\par}%
|
||||||
|
\nobreak
|
||||||
|
\vskip 3ex
|
||||||
|
\@afterheading}
|
||||||
|
\newcommand\section{\@startsection {section}{1}{\z@}%
|
||||||
|
{-3.5ex \@plus -1ex \@minus -.2ex}%
|
||||||
|
{2.3ex \@plus.2ex}%
|
||||||
|
{\normalfont\Large\bfseries}}
|
||||||
|
\newcommand\subsection{\@startsection{subsection}{2}{\z@}%
|
||||||
|
{-3.25ex\@plus -1ex \@minus -.2ex}%
|
||||||
|
{1.5ex \@plus .2ex}%
|
||||||
|
{\normalfont\large\bfseries}}
|
||||||
|
\newcommand\subsubsection{\@startsection{subsubsection}{3}{\z@}%
|
||||||
|
{-3.25ex\@plus -1ex \@minus -.2ex}%
|
||||||
|
{1.5ex \@plus .2ex}%
|
||||||
|
{\normalfont\normalsize\bfseries}}
|
||||||
|
\newcommand\paragraph{\@startsection{paragraph}{4}{\z@}%
|
||||||
|
{3.25ex \@plus1ex \@minus.2ex}%
|
||||||
|
{-1em}%
|
||||||
|
{\normalfont\normalsize\bfseries}}
|
||||||
|
\newcommand\subparagraph{\@startsection{subparagraph}{5}{\parindent}%
|
||||||
|
{3.25ex \@plus1ex \@minus .2ex}%
|
||||||
|
{-1em}%
|
||||||
|
{\normalfont\normalsize\bfseries}}
|
||||||
|
\if@twocolumn
|
||||||
|
\setlength\leftmargini {2em}
|
||||||
|
\else
|
||||||
|
\setlength\leftmargini {2.5em}
|
||||||
|
\fi
|
||||||
|
\leftmargin \leftmargini
|
||||||
|
\setlength\leftmarginii {2.2em}
|
||||||
|
\setlength\leftmarginiii {1.87em}
|
||||||
|
\setlength\leftmarginiv {1.7em}
|
||||||
|
\if@twocolumn
|
||||||
|
\setlength\leftmarginv {.5em}
|
||||||
|
\setlength\leftmarginvi {.5em}
|
||||||
|
\else
|
||||||
|
\setlength\leftmarginv {1em}
|
||||||
|
\setlength\leftmarginvi {1em}
|
||||||
|
\fi
|
||||||
|
\setlength \labelsep {.5em}
|
||||||
|
\setlength \labelwidth{\leftmargini}
|
||||||
|
\addtolength\labelwidth{-\labelsep}
|
||||||
|
\@beginparpenalty -\@lowpenalty
|
||||||
|
\@endparpenalty -\@lowpenalty
|
||||||
|
\@itempenalty -\@lowpenalty
|
||||||
|
\renewcommand\theenumi{\@arabic\c@enumi}
|
||||||
|
\renewcommand\theenumii{\@alph\c@enumii}
|
||||||
|
\renewcommand\theenumiii{\@roman\c@enumiii}
|
||||||
|
\renewcommand\theenumiv{\@Alph\c@enumiv}
|
||||||
|
\newcommand\labelenumi{\theenumi.}
|
||||||
|
\newcommand\labelenumii{(\theenumii)}
|
||||||
|
\newcommand\labelenumiii{\theenumiii.}
|
||||||
|
\newcommand\labelenumiv{\theenumiv.}
|
||||||
|
\renewcommand\p@enumii{\theenumi}
|
||||||
|
\renewcommand\p@enumiii{\theenumi(\theenumii)}
|
||||||
|
\renewcommand\p@enumiv{\p@enumiii\theenumiii}
|
||||||
|
\newcommand\labelitemi{\textbullet}
|
||||||
|
\newcommand\labelitemii{\normalfont\bfseries \textendash}
|
||||||
|
\newcommand\labelitemiii{\textasteriskcentered}
|
||||||
|
\newcommand\labelitemiv{\textperiodcentered}
|
||||||
|
\newenvironment{description}
|
||||||
|
{\list{}{\labelwidth\z@ \itemindent-\leftmargin
|
||||||
|
\let\makelabel\descriptionlabel}}
|
||||||
|
{\endlist}
|
||||||
|
\newcommand*\descriptionlabel[1]{\hspace\labelsep
|
||||||
|
\normalfont\bfseries #1}
|
||||||
|
\if@titlepage
|
||||||
|
\newenvironment{abstract}{%
|
||||||
|
\titlepage
|
||||||
|
\null\vfil
|
||||||
|
\@beginparpenalty\@lowpenalty
|
||||||
|
\begin{center}%
|
||||||
|
\bfseries \abstractname
|
||||||
|
\@endparpenalty\@M
|
||||||
|
\end{center}}%
|
||||||
|
{\par\vfil\null\endtitlepage}
|
||||||
|
\else
|
||||||
|
\newenvironment{abstract}{%
|
||||||
|
\if@twocolumn
|
||||||
|
\section*{\abstractname}%
|
||||||
|
\else
|
||||||
|
\small
|
||||||
|
\begin{center}%
|
||||||
|
{\bfseries \abstractname\vspace{-.5em}\vspace{\z@}}%
|
||||||
|
\end{center}%
|
||||||
|
\quotation
|
||||||
|
\fi}
|
||||||
|
{\if@twocolumn\else\endquotation\fi}
|
||||||
|
\fi
|
||||||
|
\newenvironment{verse}
|
||||||
|
{\let\\\@centercr
|
||||||
|
\list{}{\itemsep \z@
|
||||||
|
\itemindent -1.5em%
|
||||||
|
\listparindent\itemindent
|
||||||
|
\rightmargin \leftmargin
|
||||||
|
\advance\leftmargin 1.5em}%
|
||||||
|
\item\relax}
|
||||||
|
{\endlist}
|
||||||
|
\newenvironment{quotation}
|
||||||
|
{\list{}{\listparindent 1.5em%
|
||||||
|
\itemindent \listparindent
|
||||||
|
\rightmargin \leftmargin
|
||||||
|
\parsep \z@ \@plus\p@}%
|
||||||
|
\item\relax}
|
||||||
|
{\endlist}
|
||||||
|
\newenvironment{quote}
|
||||||
|
{\list{}{\rightmargin\leftmargin}%
|
||||||
|
\item\relax}
|
||||||
|
{\endlist}
|
||||||
|
\if@compatibility
|
||||||
|
\newenvironment{titlepage}
|
||||||
|
{%
|
||||||
|
\if@twocolumn
|
||||||
|
\@restonecoltrue\onecolumn
|
||||||
|
\else
|
||||||
|
\@restonecolfalse\newpage
|
||||||
|
\fi
|
||||||
|
\thispagestyle{empty}%
|
||||||
|
\setcounter{page}\z@
|
||||||
|
}%
|
||||||
|
{\if@restonecol\twocolumn \else \newpage \fi
|
||||||
|
}
|
||||||
|
\else
|
||||||
|
\newenvironment{titlepage}
|
||||||
|
{%
|
||||||
|
\if@twocolumn
|
||||||
|
\@restonecoltrue\onecolumn
|
||||||
|
\else
|
||||||
|
\@restonecolfalse\newpage
|
||||||
|
\fi
|
||||||
|
\thispagestyle{empty}%
|
||||||
|
\setcounter{page}\@ne
|
||||||
|
}%
|
||||||
|
{\if@restonecol\twocolumn \else \newpage \fi
|
||||||
|
\if@twoside\else
|
||||||
|
\setcounter{page}\@ne
|
||||||
|
\fi
|
||||||
|
}
|
||||||
|
\fi
|
||||||
|
\newcommand\appendix{\par
|
||||||
|
\setcounter{section}{0}%
|
||||||
|
\setcounter{subsection}{0}%
|
||||||
|
\gdef\thesection{\@Alph\c@section}}
|
||||||
|
\setlength\arraycolsep{5\p@}
|
||||||
|
\setlength\tabcolsep{6\p@}
|
||||||
|
\setlength\arrayrulewidth{.4\p@}
|
||||||
|
\setlength\doublerulesep{2\p@}
|
||||||
|
\setlength\tabbingsep{\labelsep}
|
||||||
|
\skip\@mpfootins = \skip\footins
|
||||||
|
\setlength\fboxsep{3\p@}
|
||||||
|
\setlength\fboxrule{.4\p@}
|
||||||
|
\renewcommand \theequation {\@arabic\c@equation}
|
||||||
|
\newcounter{figure}
|
||||||
|
\renewcommand \thefigure {\@arabic\c@figure}
|
||||||
|
\def\fps@figure{tbp}
|
||||||
|
\def\ftype@figure{1}
|
||||||
|
\def\ext@figure{lof}
|
||||||
|
\def\fnum@figure{\figurename\nobreakspace\thefigure}
|
||||||
|
\newenvironment{figure}
|
||||||
|
{\@float{figure}}
|
||||||
|
{\end@float}
|
||||||
|
\newenvironment{figure*}
|
||||||
|
{\@dblfloat{figure}}
|
||||||
|
{\end@dblfloat}
|
||||||
|
\newcounter{table}
|
||||||
|
\renewcommand\thetable{\@arabic\c@table}
|
||||||
|
\def\fps@table{tbp}
|
||||||
|
\def\ftype@table{2}
|
||||||
|
\def\ext@table{lot}
|
||||||
|
\def\fnum@table{\tablename\nobreakspace\thetable}
|
||||||
|
\newenvironment{table}
|
||||||
|
{\@float{table}}
|
||||||
|
{\end@float}
|
||||||
|
\newenvironment{table*}
|
||||||
|
{\@dblfloat{table}}
|
||||||
|
{\end@dblfloat}
|
||||||
|
\newlength\abovecaptionskip
|
||||||
|
\newlength\belowcaptionskip
|
||||||
|
\setlength\abovecaptionskip{10\p@}
|
||||||
|
\setlength\belowcaptionskip{0\p@}
|
||||||
|
\long\def\@makecaption#1#2{%
|
||||||
|
\vskip\abovecaptionskip
|
||||||
|
\sbox\@tempboxa{#1: #2}%
|
||||||
|
\ifdim \wd\@tempboxa >\hsize
|
||||||
|
#1: #2\par
|
||||||
|
\else
|
||||||
|
\global \@minipagefalse
|
||||||
|
\hb@xt@\hsize{\hfil\box\@tempboxa\hfil}%
|
||||||
|
\fi
|
||||||
|
\vskip\belowcaptionskip}
|
||||||
|
\DeclareOldFontCommand{\rm}{\normalfont\rmfamily}{\mathrm}
|
||||||
|
\DeclareOldFontCommand{\sf}{\normalfont\sffamily}{\mathsf}
|
||||||
|
\DeclareOldFontCommand{\tt}{\normalfont\ttfamily}{\mathtt}
|
||||||
|
\DeclareOldFontCommand{\bf}{\normalfont\bfseries}{\mathbf}
|
||||||
|
\DeclareOldFontCommand{\it}{\normalfont\itshape}{\mathit}
|
||||||
|
\DeclareOldFontCommand{\sl}{\normalfont\slshape}{\@nomath\sl}
|
||||||
|
\DeclareOldFontCommand{\sc}{\normalfont\scshape}{\@nomath\sc}
|
||||||
|
\DeclareRobustCommand*\cal{\@fontswitch\relax\mathcal}
|
||||||
|
\DeclareRobustCommand*\mit{\@fontswitch\relax\mathnormal}
|
||||||
|
\newcommand\@pnumwidth{1.55em}
|
||||||
|
\newcommand\@tocrmarg{2.55em}
|
||||||
|
\newcommand\@dotsep{4.5}
|
||||||
|
\setcounter{tocdepth}{3}
|
||||||
|
\newcommand\tableofcontents{%
|
||||||
|
\section*{\contentsname
|
||||||
|
\@mkboth{%
|
||||||
|
\MakeUppercase\contentsname}{\MakeUppercase\contentsname}}%
|
||||||
|
\@starttoc{toc}%
|
||||||
|
}
|
||||||
|
\newcommand*\l@part[2]{%
|
||||||
|
\ifnum \c@tocdepth >-2\relax
|
||||||
|
\addpenalty\@secpenalty
|
||||||
|
\addvspace{2.25em \@plus\p@}%
|
||||||
|
\setlength\@tempdima{3em}%
|
||||||
|
\begingroup
|
||||||
|
\parindent \z@ \rightskip \@pnumwidth
|
||||||
|
\parfillskip -\@pnumwidth
|
||||||
|
{\leavevmode
|
||||||
|
\large \bfseries #1\hfil \hb@xt@\@pnumwidth{\hss #2}}\par
|
||||||
|
\nobreak
|
||||||
|
\if@compatibility
|
||||||
|
\global\@nobreaktrue
|
||||||
|
\everypar{\global\@nobreakfalse\everypar{}}%
|
||||||
|
\fi
|
||||||
|
\endgroup
|
||||||
|
\fi}
|
||||||
|
\newcommand*\l@section[2]{%
|
||||||
|
\ifnum \c@tocdepth >\z@
|
||||||
|
\addpenalty\@secpenalty
|
||||||
|
\addvspace{1.0em \@plus\p@}%
|
||||||
|
\setlength\@tempdima{1.5em}%
|
||||||
|
\begingroup
|
||||||
|
\parindent \z@ \rightskip \@pnumwidth
|
||||||
|
\parfillskip -\@pnumwidth
|
||||||
|
\leavevmode \bfseries
|
||||||
|
\advance\leftskip\@tempdima
|
||||||
|
\hskip -\leftskip
|
||||||
|
#1\nobreak\hfil \nobreak\hb@xt@\@pnumwidth{\hss #2}\par
|
||||||
|
\endgroup
|
||||||
|
\fi}
|
||||||
|
\newcommand*\l@subsection{\@dottedtocline{2}{1.5em}{2.3em}}
|
||||||
|
\newcommand*\l@subsubsection{\@dottedtocline{3}{3.8em}{3.2em}}
|
||||||
|
\newcommand*\l@paragraph{\@dottedtocline{4}{7.0em}{4.1em}}
|
||||||
|
\newcommand*\l@subparagraph{\@dottedtocline{5}{10em}{5em}}
|
||||||
|
\newcommand\listoffigures{%
|
||||||
|
\section*{\listfigurename}%
|
||||||
|
\@mkboth{\MakeUppercase\listfigurename}%
|
||||||
|
{\MakeUppercase\listfigurename}%
|
||||||
|
\@starttoc{lof}%
|
||||||
|
}
|
||||||
|
\newcommand*\l@figure{\@dottedtocline{1}{1.5em}{2.3em}}
|
||||||
|
\newcommand\listoftables{%
|
||||||
|
\section*{\listtablename}%
|
||||||
|
\@mkboth{%
|
||||||
|
\MakeUppercase\listtablename}%
|
||||||
|
{\MakeUppercase\listtablename}%
|
||||||
|
\@starttoc{lot}%
|
||||||
|
}
|
||||||
|
\let\l@table\l@figure
|
||||||
|
\newdimen\bibindent
|
||||||
|
\setlength\bibindent{1.5em}
|
||||||
|
\newenvironment{thebibliography}[1]
|
||||||
|
{\section*{\refname}%
|
||||||
|
\@mkboth{\MakeUppercase\refname}{\MakeUppercase\refname}%
|
||||||
|
\list{\@biblabel{\@arabic\c@enumiv}}%
|
||||||
|
{\settowidth\labelwidth{\@biblabel{#1}}%
|
||||||
|
\leftmargin\labelwidth
|
||||||
|
\advance\leftmargin\labelsep
|
||||||
|
\@openbib@code
|
||||||
|
\usecounter{enumiv}%
|
||||||
|
\let\p@enumiv\@empty
|
||||||
|
\renewcommand\theenumiv{\@arabic\c@enumiv}}%
|
||||||
|
\sloppy
|
||||||
|
\clubpenalty4000
|
||||||
|
\@clubpenalty \clubpenalty
|
||||||
|
\widowpenalty4000%
|
||||||
|
\sfcode`\.\@m}
|
||||||
|
{\def\@noitemerr
|
||||||
|
{\@latex@warning{Empty `thebibliography' environment}}%
|
||||||
|
\endlist}
|
||||||
|
\newcommand\newblock{\hskip .11em\@plus.33em\@minus.07em}
|
||||||
|
\let\@openbib@code\@empty
|
||||||
|
\newenvironment{theindex}
|
||||||
|
{\if@twocolumn
|
||||||
|
\@restonecolfalse
|
||||||
|
\else
|
||||||
|
\@restonecoltrue
|
||||||
|
\fi
|
||||||
|
\twocolumn[\section*{\indexname}]%
|
||||||
|
\@mkboth{\MakeUppercase\indexname}%
|
||||||
|
{\MakeUppercase\indexname}%
|
||||||
|
\thispagestyle{plain}\parindent\z@
|
||||||
|
\parskip\z@ \@plus .3\p@\relax
|
||||||
|
\columnseprule \z@
|
||||||
|
\columnsep 35\p@
|
||||||
|
\let\item\@idxitem}
|
||||||
|
{\if@restonecol\onecolumn\else\clearpage\fi}
|
||||||
|
\newcommand\@idxitem{\par\hangindent 40\p@}
|
||||||
|
\newcommand\subitem{\@idxitem \hspace*{20\p@}}
|
||||||
|
\newcommand\subsubitem{\@idxitem \hspace*{30\p@}}
|
||||||
|
\newcommand\indexspace{\par \vskip 10\p@ \@plus5\p@ \@minus3\p@\relax}
|
||||||
|
\renewcommand\footnoterule{%
|
||||||
|
\kern-3\p@
|
||||||
|
\hrule\@width.4\columnwidth
|
||||||
|
\kern2.6\p@}
|
||||||
|
\newcommand\@makefntext[1]{%
|
||||||
|
\parindent 1em%
|
||||||
|
\noindent
|
||||||
|
\hb@xt@1.8em{\hss\@makefnmark}#1}
|
||||||
|
\newcommand\contentsname{Contents}
|
||||||
|
\newcommand\listfigurename{List of Figures}
|
||||||
|
\newcommand\listtablename{List of Tables}
|
||||||
|
\newcommand\refname{References}
|
||||||
|
\newcommand\indexname{Index}
|
||||||
|
\newcommand\figurename{Figure}
|
||||||
|
\newcommand\tablename{Table}
|
||||||
|
\newcommand\partname{Part}
|
||||||
|
\newcommand\appendixname{Appendix}
|
||||||
|
\newcommand\abstractname{}
|
||||||
|
\def\today{\ifcase\month\or
|
||||||
|
January\or February\or March\or April\or May\or June\or
|
||||||
|
July\or August\or September\or October\or November\or December\fi
|
||||||
|
\space\number\day, \number\year}
|
||||||
|
\setlength\columnsep{10\p@}
|
||||||
|
\setlength\columnseprule{0\p@}
|
||||||
|
\pagestyle{plain}
|
||||||
|
\pagenumbering{arabic}
|
||||||
|
|
||||||
|
% === ARLIMS adaptions following ============================================
|
||||||
|
\pagestyle{empty}
|
||||||
|
|
||||||
|
\setlength{\oddsidemargin}{1.5 cm}
|
||||||
|
\setlength{\evensidemargin}{-0.5 cm}
|
||||||
|
\setlength{\textheight}{22 cm}
|
||||||
|
\setlength{\textwidth}{15 cm}
|
||||||
|
\setlength{\topmargin}{0 cm}
|
||||||
|
|
||||||
|
% --- special indexing to cope with the list of articles
|
||||||
|
\newif\ifIndexMade
|
||||||
|
\def\makeartIndex{%
|
||||||
|
\protected@write1{}{\string\global\string\IndexMadetrue}
|
||||||
|
\newwrite\@artIndexfile
|
||||||
|
\immediate\openout\@artIndexfile=\jobname.adx
|
||||||
|
\def\artIndex{\@bsphack\begingroup\@sanitize\@wrartIndex}
|
||||||
|
\typeout{Writing artIndex file \jobname.adx}%
|
||||||
|
\let\makeartIndex\@empty
|
||||||
|
}
|
||||||
|
|
||||||
|
\def\@wrartIndex#1{%
|
||||||
|
\protected@write\@artIndexfile{}{%
|
||||||
|
\string\artIndexentry{\factTitle}%
|
||||||
|
{\thepage}%
|
||||||
|
{\string\last#1}%
|
||||||
|
{\factAuthor\factAuthorB\factAuthorC\factAuthorD}}%
|
||||||
|
\endgroup%
|
||||||
|
\@esphack%
|
||||||
|
}
|
||||||
|
|
||||||
|
\def\artIndex{\@bsphack\begingroup \@sanitize\@artIndex}
|
||||||
|
\def\@artIndex#1{\endgroup\@esphack}
|
||||||
|
|
||||||
|
\def\artIndexLast#1{%
|
||||||
|
\protected@write1{}{\string\gdef\string\last#1{\thepage}}%
|
||||||
|
}
|
||||||
|
|
||||||
|
% --- to set up the headers permanently
|
||||||
|
\newcommand{\markperm}[2]{%
|
||||||
|
\global\def\@evenhead{\arabic{page}\hfil{#1}}%
|
||||||
|
\global\def\@oddhead{{#2}\hfil\arabic{page}}
|
||||||
|
}
|
||||||
|
% (yes, I know I've hardwired arabic page numbering)
|
||||||
|
|
||||||
|
% --- to set up the headers with a once off first
|
||||||
|
\newcommand{\markonce}[3]{%
|
||||||
|
\global\def\@evenhead{\arabic{page}\hfil{#1}\markperm{#2}{#3}}%
|
||||||
|
\global\def\@oddhead{{#1}\hfil\arabic{page}\markperm{#2}{#3}}%
|
||||||
|
}
|
||||||
|
|
||||||
|
%% Put a line after the abstract %%
|
||||||
|
\newcommand{\drawline}{%
|
||||||
|
\vspace*{1mm}
|
||||||
|
\begin{center}
|
||||||
|
\rule{\textwidth}{0.2mm}
|
||||||
|
\end{center}
|
||||||
|
\vspace*{1mm}
|
||||||
|
}
|
||||||
|
|
||||||
|
\newcommand{\reff}[1]{(\ref{#1})}
|
||||||
|
|
||||||
|
|
||||||
|
% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
% Define the author and title commands. Bascially each just stuffs
|
||||||
|
% the argument into a storage command. I need to define the blank
|
||||||
|
% storage commands because I am using the "renewcomand", since I
|
||||||
|
% need to do this for each article.
|
||||||
|
|
||||||
|
\def\author{}
|
||||||
|
\def\institute{}
|
||||||
|
|
||||||
|
\def\factTitle{}
|
||||||
|
|
||||||
|
\def\factAuthor{}
|
||||||
|
\def\factAuthorB{}
|
||||||
|
\def\factAuthorC{}
|
||||||
|
\def\factAuthorD{}
|
||||||
|
|
||||||
|
\def\factInstitute{}
|
||||||
|
\def\factInstituteB{}
|
||||||
|
\def\factInstituteC{}
|
||||||
|
\def\factInstituteD{}
|
||||||
|
|
||||||
|
\def\footInst{}
|
||||||
|
\def\footInstB{}
|
||||||
|
\def\footInstC{}
|
||||||
|
\def\footInstD{}
|
||||||
|
|
||||||
|
\def\footAuthorInst{}
|
||||||
|
\def\footAuthorInstB{}
|
||||||
|
\def\footAuthorInstC{}
|
||||||
|
\def\footAuthorInstD{}
|
||||||
|
|
||||||
|
\renewcommand{\author}[2][]{
|
||||||
|
\renewcommand{\footAuthorInst}{#1}
|
||||||
|
\renewcommand{\factAuthor}{#2}
|
||||||
|
}
|
||||||
|
|
||||||
|
\newcommand{\authorB}[2][]{
|
||||||
|
\renewcommand{\footAuthorInstB}{#1}
|
||||||
|
\renewcommand{\factAuthorB}{#2}
|
||||||
|
}
|
||||||
|
|
||||||
|
\newcommand{\authorC}[2][]{
|
||||||
|
\renewcommand{\footAuthorInstC}{#1}
|
||||||
|
\renewcommand{\factAuthorC}{#2}
|
||||||
|
}
|
||||||
|
|
||||||
|
\newcommand{\authorD}[2][]{
|
||||||
|
\renewcommand{\footAuthorInstD}{#1}
|
||||||
|
\renewcommand{\factAuthorD}{#2}
|
||||||
|
}
|
||||||
|
|
||||||
|
\renewcommand{\institute}[2][]{
|
||||||
|
\renewcommand{\footInst}{#1}
|
||||||
|
\renewcommand{\factInstitute}{#2}
|
||||||
|
}
|
||||||
|
|
||||||
|
\newcommand{\instituteB}[2][*]{
|
||||||
|
\renewcommand{\footInstB}{#1}
|
||||||
|
\renewcommand{\factInstituteB}{#2}
|
||||||
|
}
|
||||||
|
|
||||||
|
\newcommand{\instituteC}[2][**]{
|
||||||
|
\renewcommand{\footInstC}{#1}
|
||||||
|
\renewcommand{\factInstituteC}{#2}
|
||||||
|
}
|
||||||
|
|
||||||
|
\newcommand{\instituteD}[2][***]{
|
||||||
|
\renewcommand{\footInstD}{#1}
|
||||||
|
\renewcommand{\factInstituteD}{#2}
|
||||||
|
}
|
||||||
|
|
||||||
|
\def\nameTag{}
|
||||||
|
|
||||||
|
\renewcommand{\title}[1]{
|
||||||
|
\renewcommand{\factTitle}{#1}
|
||||||
|
}
|
||||||
|
|
||||||
|
\newcommand{\iimsaddress}{
|
||||||
|
\begin{center}
|
||||||
|
\textit{Institute of Information \& Mathematical Sciences\\
|
||||||
|
Massey University at Albany, Auckland, New Zealand.}
|
||||||
|
\end{center}
|
||||||
|
}
|
||||||
|
|
||||||
|
\newcommand{\maketitle}[1][\factTitle]{
|
||||||
|
\begin{center}
|
||||||
|
\markonce{
|
||||||
|
\parbox{10cm}{\small
|
||||||
|
\textit{The Python Papers,} Vol.~3, No.~3 (2008)\\[1mm]
|
||||||
|
Available online at http://ojs.pythonpapers.org/index.php/tpp/issue/view/10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{\factAuthor\factAuthorB\factAuthorC\factAuthorD}
|
||||||
|
{#1}
|
||||||
|
|
||||||
|
\vspace*{1cm}
|
||||||
|
|
||||||
|
\begin{minipage}{12cm}
|
||||||
|
\begin{center}
|
||||||
|
{\bfseries\Large\factTitle\par}
|
||||||
|
\vspace*{2mm}
|
||||||
|
\end{center}
|
||||||
|
\end{minipage}
|
||||||
|
|
||||||
|
\vspace*{3mm}
|
||||||
|
|
||||||
|
\textsc{%
|
||||||
|
\factAuthor $^{\footAuthorInst}$%
|
||||||
|
\factAuthorB $^{\footAuthorInstB}$%
|
||||||
|
\factAuthorC $^{\footAuthorInstC}$%
|
||||||
|
\factAuthorD $^{\footAuthorInstD}$%
|
||||||
|
}
|
||||||
|
|
||||||
|
\vspace*{1mm}
|
||||||
|
|
||||||
|
$^{\footInst}$\textit{\factInstitute}
|
||||||
|
$^{\footInstB}$\textit{\factInstituteB}
|
||||||
|
$^{\footInstC}$\textit{\factInstituteC}
|
||||||
|
$^{\footInstD}$\textit{\factInstituteD}
|
||||||
|
|
||||||
|
\artIndex{\nameTag}
|
||||||
|
\end{center}
|
||||||
|
}
|
||||||
|
|
||||||
|
% calculate the margins so that the text on opposite sides
|
||||||
|
% of the same sheet is lined up at the edges
|
||||||
|
% ok, its a fudge, but you can change the oddsidemargin
|
||||||
|
% to shift both sides in sync.
|
||||||
|
|
||||||
|
\oddsidemargin1cm
|
||||||
|
\evensidemargin\paperwidth
|
||||||
|
\addtolength{\evensidemargin}{-\oddsidemargin}
|
||||||
|
\addtolength{\evensidemargin}{-205mm}
|
||||||
|
|
||||||
|
% --- turn off a few things -------------------------------------------------
|
||||||
|
\renewcommand{\markboth}[2]{}
|
||||||
|
\renewcommand{\markright}[1]{}
|
||||||
|
\newcommand{\markheading}[1]{}
|
||||||
|
|
||||||
|
\usepackage{amsmath,amsfonts,dsfont}
|
||||||
|
\usepackage{amsthm}
|
||||||
|
|
||||||
|
\newif\ifCombinedRlims
|
||||||
|
|
||||||
|
% === end of ARLIMS adaptions ===============================================
|
||||||
|
|
||||||
|
\if@twoside
|
||||||
|
\else
|
||||||
|
\raggedbottom
|
||||||
|
\fi
|
||||||
|
\if@twocolumn
|
||||||
|
\twocolumn
|
||||||
|
\sloppy
|
||||||
|
\flushbottom
|
||||||
|
\else
|
||||||
|
\onecolumn
|
||||||
|
\fi
|
||||||
|
\endinput
|
||||||
|
%%
|
||||||
|
%% End of file `arlims.cls'.
|
||||||
898
doc/KPyCon2009/arlimsTPPM.cls
Normal file
@@ -0,0 +1,898 @@
|
|||||||
|
%%
|
||||||
|
%% This is file `arlims.cls',
|
||||||
|
%% It is based on the original `article.cls' from a tetex 3.0
|
||||||
|
%% distribution (Ubuntu tetex-base 3.0-19 package).
|
||||||
|
%%
|
||||||
|
%% Additions to it were made by (probably) various authors at the
|
||||||
|
%% Massey University Institute of Information and Mathematical
|
||||||
|
%% Sciences.
|
||||||
|
%%
|
||||||
|
%% For further information contact Heath James
|
||||||
|
%% <h.a.james@massey.ac.nz>, Guy Kloss <g.kloss@massey.ac.nz>, Paul
|
||||||
|
%% Cowpertwait <p.s.cowpertwait@massey.ac.nz> or anybody you expect
|
||||||
|
%% suitable for this purpose ...
|
||||||
|
%%
|
||||||
|
%% Copyright 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004
|
||||||
|
%% The LaTeX3 Project and any individual authors listed elsewhere
|
||||||
|
%% in this file.
|
||||||
|
%%
|
||||||
|
%% This file was generated from file(s) of the LaTeX base system.
|
||||||
|
%% Numerous changes were made to be fit for RLIMS.
|
||||||
|
%% --------------------------------------------------------------
|
||||||
|
%%
|
||||||
|
%% Changes:
|
||||||
|
%%
|
||||||
|
%% * numerous changes previously to fit article.sty for the purpose
|
||||||
|
%%
|
||||||
|
%% version 1.5:
|
||||||
|
%%
|
||||||
|
%% * fixed the instructions
|
||||||
|
%% * added better default options to the template
|
||||||
|
%% * changed the year to 2008
|
||||||
|
%%
|
||||||
|
%% version 1.4:
|
||||||
|
%%
|
||||||
|
%% * fitted to a newer version of article.sty
|
||||||
|
%% * cleanups in layout and formatting
|
||||||
|
%% * better compliance to LaTeX2e
|
||||||
|
%% * re-fitted \institute, \instituteB, ...C, ...D
|
||||||
|
%% it doesn't work 100% nicely, but it works)
|
||||||
|
%% * removed dependency on natbib.sty (so cite.sty, or else can be
|
||||||
|
%% used as liked)
|
||||||
|
%% * changed paper size to a4paper by default
|
||||||
|
%% * removed dependencies to graphicx.sty and subfigure.sty
|
||||||
|
%% (users like to choose and do that themselves)
|
||||||
|
%%
|
||||||
|
%% --------------------------------------------------------------
|
||||||
|
%%
|
||||||
|
%% It may be distributed and/or modified under the
|
||||||
|
%% conditions of the LaTeX Project Public License, either version 1.3
|
||||||
|
%% of this license or (at your option) any later version.
|
||||||
|
%% The latest version of this license is in
|
||||||
|
%% http://www.latex-project.org/lppl.txt
|
||||||
|
%% and version 1.3 or later is part of all distributions of LaTeX
|
||||||
|
%% version 2003/12/01 or later.
|
||||||
|
%%
|
||||||
|
%% This file has the LPPL maintenance status "maintained".
|
||||||
|
%%
|
||||||
|
%% This file may only be distributed together with a copy of the LaTeX
|
||||||
|
%% base system. You may however distribute the LaTeX base system without
|
||||||
|
%% such generated files.
|
||||||
|
%%
|
||||||
|
%% The list of all files belonging to the LaTeX base distribution is
|
||||||
|
%% given in the file `manifest.txt'. See also `legal.txt' for additional
|
||||||
|
%% information.
|
||||||
|
%%
|
||||||
|
%% The list of derived (unpacked) files belonging to the distribution
|
||||||
|
%% and covered by LPPL is defined by the unpacking scripts (with
|
||||||
|
%% extension .ins) which are part of the distribution.
|
||||||
|
%% \CharacterTable
|
||||||
|
%% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z
|
||||||
|
%% Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z
|
||||||
|
%% Digits \0\1\2"s\4\5\6\7\8\9
|
||||||
|
%% Exclamation \! Double quote \" Hash (number) \#
|
||||||
|
%% Dollar \$ Percent \% Ampersand \&
|
||||||
|
%% Acute accent \' Left paren \( Right paren \)
|
||||||
|
%% Asterisk \* Plus \+ Comma \,
|
||||||
|
%% Minus \- Point \. Solidus \/
|
||||||
|
%% Colon \: Semicolon \; Less than \<
|
||||||
|
%% Equals \= Greater than \> Question mark \?
|
||||||
|
%% Commercial at \@ Left bracket \[ Backslash \\
|
||||||
|
%% Right bracket \] Circumflex \^ Underscore \_
|
||||||
|
%% Grave accent \` Left brace \{ Vertical bar \|
|
||||||
|
%% Right brace \} Tilde \~}
|
||||||
|
\NeedsTeXFormat{LaTeX2e}[1995/12/01]
|
||||||
|
\ProvidesClass{arlims}
|
||||||
|
[2008/04/23 v1.5
|
||||||
|
Massey Research Letters Article Class]
|
||||||
|
\newcommand\@ptsize{}
|
||||||
|
\newif\if@restonecol
|
||||||
|
\newif\if@titlepage
|
||||||
|
\@titlepagefalse
|
||||||
|
\if@compatibility\else
|
||||||
|
\DeclareOption{a4paper}
|
||||||
|
{\setlength\paperheight {297mm}%
|
||||||
|
\setlength\paperwidth {210mm}}
|
||||||
|
\DeclareOption{a5paper}
|
||||||
|
{\setlength\paperheight {210mm}%
|
||||||
|
\setlength\paperwidth {148mm}}
|
||||||
|
\DeclareOption{b5paper}
|
||||||
|
{\setlength\paperheight {250mm}%
|
||||||
|
\setlength\paperwidth {176mm}}
|
||||||
|
\DeclareOption{letterpaper}
|
||||||
|
{\setlength\paperheight {11in}%
|
||||||
|
\setlength\paperwidth {8.5in}}
|
||||||
|
\DeclareOption{legalpaper}
|
||||||
|
{\setlength\paperheight {14in}%
|
||||||
|
\setlength\paperwidth {8.5in}}
|
||||||
|
\DeclareOption{executivepaper}
|
||||||
|
{\setlength\paperheight {10.5in}%
|
||||||
|
\setlength\paperwidth {7.25in}}
|
||||||
|
\DeclareOption{landscape}
|
||||||
|
{\setlength\@tempdima {\paperheight}%
|
||||||
|
\setlength\paperheight {\paperwidth}%
|
||||||
|
\setlength\paperwidth {\@tempdima}}
|
||||||
|
\fi
|
||||||
|
\if@compatibility
|
||||||
|
\renewcommand\@ptsize{0}
|
||||||
|
\else
|
||||||
|
\DeclareOption{10pt}{\renewcommand\@ptsize{0}}
|
||||||
|
\fi
|
||||||
|
\DeclareOption{11pt}{\renewcommand\@ptsize{1}}
|
||||||
|
\DeclareOption{12pt}{\renewcommand\@ptsize{2}}
|
||||||
|
\if@compatibility\else
|
||||||
|
\DeclareOption{oneside}{\@twosidefalse \@mparswitchfalse}
|
||||||
|
\fi
|
||||||
|
\DeclareOption{twoside}{\@twosidetrue \@mparswitchtrue}
|
||||||
|
\DeclareOption{draft}{\setlength\overfullrule{5pt}}
|
||||||
|
\if@compatibility\else
|
||||||
|
\DeclareOption{final}{\setlength\overfullrule{0pt}}
|
||||||
|
\fi
|
||||||
|
\DeclareOption{titlepage}{\@titlepagetrue}
|
||||||
|
\if@compatibility\else
|
||||||
|
\DeclareOption{notitlepage}{\@titlepagefalse}
|
||||||
|
\fi
|
||||||
|
\if@compatibility\else
|
||||||
|
\DeclareOption{onecolumn}{\@twocolumnfalse}
|
||||||
|
\fi
|
||||||
|
\DeclareOption{twocolumn}{\@twocolumntrue}
|
||||||
|
\DeclareOption{leqno}{\input{leqno.clo}}
|
||||||
|
\DeclareOption{fleqn}{\input{fleqn.clo}}
|
||||||
|
\DeclareOption{openbib}{%
|
||||||
|
\AtEndOfPackage{%
|
||||||
|
\renewcommand\@openbib@code{%
|
||||||
|
\advance\leftmargin\bibindent
|
||||||
|
\itemindent -\bibindent
|
||||||
|
\listparindent \itemindent
|
||||||
|
\parsep \z@
|
||||||
|
}%
|
||||||
|
\renewcommand\newblock{\par}}%
|
||||||
|
}
|
||||||
|
\ExecuteOptions{a4paper,10pt,oneside,onecolumn,final}
|
||||||
|
\ProcessOptions
|
||||||
|
\input{size1\@ptsize.clo}
|
||||||
|
\setlength\lineskip{1\p@}
|
||||||
|
\setlength\normallineskip{1\p@}
|
||||||
|
\renewcommand\baselinestretch{}
|
||||||
|
\setlength\parskip{0\p@ \@plus \p@}
|
||||||
|
\@lowpenalty 51
|
||||||
|
\@medpenalty 151
|
||||||
|
\@highpenalty 301
|
||||||
|
\setcounter{topnumber}{2}
|
||||||
|
\renewcommand\topfraction{.7}
|
||||||
|
\setcounter{bottomnumber}{1}
|
||||||
|
\renewcommand\bottomfraction{.3}
|
||||||
|
\setcounter{totalnumber}{3}
|
||||||
|
\renewcommand\textfraction{.2}
|
||||||
|
\renewcommand\floatpagefraction{.5}
|
||||||
|
\setcounter{dbltopnumber}{2}
|
||||||
|
\renewcommand\dbltopfraction{.7}
|
||||||
|
\renewcommand\dblfloatpagefraction{.5}
|
||||||
|
\if@twoside
|
||||||
|
\def\ps@headings{%
|
||||||
|
\let\@oddfoot\@empty\let\@evenfoot\@empty
|
||||||
|
\def\@evenhead{\thepage\hfil\slshape\leftmark}%
|
||||||
|
\def\@oddhead{{\slshape\rightmark}\hfil\thepage}%
|
||||||
|
\let\@mkboth\markboth
|
||||||
|
\def\sectionmark##1{%
|
||||||
|
\markboth {\MakeUppercase{%
|
||||||
|
\ifnum \c@secnumdepth >\z@
|
||||||
|
\thesection\quad
|
||||||
|
\fi
|
||||||
|
##1}}{}}%
|
||||||
|
\def\subsectionmark##1{%
|
||||||
|
\markright {%
|
||||||
|
\ifnum \c@secnumdepth >\@ne
|
||||||
|
\thesubsection\quad
|
||||||
|
\fi
|
||||||
|
##1}}}
|
||||||
|
\else
|
||||||
|
\def\ps@headings{%
|
||||||
|
\let\@oddfoot\@empty
|
||||||
|
\def\@oddhead{{\slshape\rightmark}\hfil\thepage}%
|
||||||
|
\let\@mkboth\markboth
|
||||||
|
\def\sectionmark##1{%
|
||||||
|
\markright {\MakeUppercase{%
|
||||||
|
\ifnum \c@secnumdepth >\m@ne
|
||||||
|
\thesection\quad
|
||||||
|
\fi
|
||||||
|
##1}}}}
|
||||||
|
\fi
|
||||||
|
\def\ps@myheadings{%
|
||||||
|
\let\@oddfoot\@empty\let\@evenfoot\@empty
|
||||||
|
\def\@evenhead{\thepage\hfil\slshape\leftmark}%
|
||||||
|
\def\@oddhead{{\slshape\rightmark}\hfil\thepage}%
|
||||||
|
\let\@mkboth\@gobbletwo
|
||||||
|
\let\sectionmark\@gobble
|
||||||
|
\let\subsectionmark\@gobble
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
%---------- old definition of title page -------------------------------
|
||||||
|
\newif\ifGrxlb\ifGrxlb
|
||||||
|
|
||||||
|
\if@titlepage
|
||||||
|
\newcommand\maketitle{\begin{titlepage}%
|
||||||
|
\let\footnotesize\small
|
||||||
|
\let\footnoterule\relax
|
||||||
|
\let \footnote \thanks
|
||||||
|
\null\vfil
|
||||||
|
\vskip 60\p@
|
||||||
|
\begin{center}%
|
||||||
|
{\LARGE \@title \par}%
|
||||||
|
\vskip 3em%
|
||||||
|
{\large
|
||||||
|
\lineskip .75em%
|
||||||
|
\begin{tabular}[t]{c}%
|
||||||
|
\@author
|
||||||
|
\end{tabular}\par}%
|
||||||
|
\vskip 1.5em%
|
||||||
|
{\large \@date \par}% % Set date in \large size.
|
||||||
|
\end{center}\par
|
||||||
|
\@thanks
|
||||||
|
\vfil\null
|
||||||
|
\end{titlepage}%
|
||||||
|
\setcounter{footnote}{0}%
|
||||||
|
\global\let\thanks\relax
|
||||||
|
\global\let\maketitle\relax
|
||||||
|
\global\let\@thanks\@empty
|
||||||
|
\global\let\@author\@empty
|
||||||
|
\global\let\@date\@empty
|
||||||
|
\global\let\@title\@empty
|
||||||
|
\global\let\title\relax
|
||||||
|
\global\let\author\relax
|
||||||
|
\global\let\date\relax
|
||||||
|
\global\let\and\relax
|
||||||
|
}
|
||||||
|
\else
|
||||||
|
\newcommand\maketitle{\par
|
||||||
|
\begingroup
|
||||||
|
\renewcommand\thefootnote{\@fnsymbol\c@footnote}%
|
||||||
|
\def\@makefnmark{\rlap{\@textsuperscript{\normalfont\@thefnmark}}}%
|
||||||
|
\long\def\@makefntext##1{\parindent 1em\noindent
|
||||||
|
\hb@xt@1.8em{%
|
||||||
|
\hss\@textsuperscript{\normalfont\@thefnmark}}##1}%
|
||||||
|
\if@twocolumn
|
||||||
|
\ifnum \col@number=\@ne
|
||||||
|
\@maketitle
|
||||||
|
\else
|
||||||
|
\twocolumn[\@maketitle]%
|
||||||
|
\fi
|
||||||
|
\else
|
||||||
|
\newpage
|
||||||
|
\global\@topnum\z@ % Prevents figures from going at top of page.
|
||||||
|
\@maketitle
|
||||||
|
\fi
|
||||||
|
\thispagestyle{plain}\@thanks
|
||||||
|
\endgroup
|
||||||
|
\setcounter{footnote}{0}%
|
||||||
|
\global\let\thanks\relax
|
||||||
|
\global\let\maketitle\relax
|
||||||
|
\global\let\@maketitle\relax
|
||||||
|
\global\let\@thanks\@empty
|
||||||
|
\global\let\@author\@empty
|
||||||
|
\global\let\@date\@empty
|
||||||
|
\global\let\@title\@empty
|
||||||
|
\global\let\title\relax
|
||||||
|
\global\let\author\relax
|
||||||
|
\global\let\date\relax
|
||||||
|
\global\let\and\relax
|
||||||
|
}
|
||||||
|
\def\@maketitle{%
|
||||||
|
\newpage
|
||||||
|
\null
|
||||||
|
\vskip 2em%
|
||||||
|
\begin{center}%
|
||||||
|
\let \footnote \thanks
|
||||||
|
{\LARGE \@title \par}%
|
||||||
|
\vskip 1.5em%
|
||||||
|
{\large
|
||||||
|
\lineskip .5em%
|
||||||
|
\begin{tabular}[t]{c}%
|
||||||
|
\@author
|
||||||
|
\end{tabular}\par}%
|
||||||
|
\vskip 1em%
|
||||||
|
{\large \@date}%
|
||||||
|
\end{center}%
|
||||||
|
\par
|
||||||
|
\vskip 1.5em}
|
||||||
|
\fi
|
||||||
|
|
||||||
|
\fi
|
||||||
|
%---------------- end of old definition of title page ---------------------
|
||||||
|
|
||||||
|
\setcounter{secnumdepth}{3}
|
||||||
|
\newcounter {part}
|
||||||
|
\newcounter {section}
|
||||||
|
\newcounter {subsection}[section]
|
||||||
|
\newcounter {subsubsection}[subsection]
|
||||||
|
\newcounter {paragraph}[subsubsection]
|
||||||
|
\newcounter {subparagraph}[paragraph]
|
||||||
|
\renewcommand \thepart {\@Roman\c@part}
|
||||||
|
\renewcommand \thesection {\@arabic\c@section}
|
||||||
|
\renewcommand\thesubsection {\thesection.\@arabic\c@subsection}
|
||||||
|
\renewcommand\thesubsubsection{\thesubsection .\@arabic\c@subsubsection}
|
||||||
|
\renewcommand\theparagraph {\thesubsubsection.\@arabic\c@paragraph}
|
||||||
|
\renewcommand\thesubparagraph {\theparagraph.\@arabic\c@subparagraph}
|
||||||
|
\newcommand\part{%
|
||||||
|
\if@noskipsec \leavevmode \fi
|
||||||
|
\par
|
||||||
|
\addvspace{4ex}%
|
||||||
|
\@afterindentfalse
|
||||||
|
\secdef\@part\@spart}
|
||||||
|
|
||||||
|
\def\@part[#1]#2{%
|
||||||
|
\ifnum \c@secnumdepth >\m@ne
|
||||||
|
\refstepcounter{part}%
|
||||||
|
\addcontentsline{toc}{part}{\thepart\hspace{1em}#1}%
|
||||||
|
\else
|
||||||
|
\addcontentsline{toc}{part}{#1}%
|
||||||
|
\fi
|
||||||
|
{\parindent \z@ \raggedright
|
||||||
|
\interlinepenalty \@M
|
||||||
|
\normalfont
|
||||||
|
\ifnum \c@secnumdepth >\m@ne
|
||||||
|
\Large\bfseries \partname\nobreakspace\thepart
|
||||||
|
\par\nobreak
|
||||||
|
\fi
|
||||||
|
\huge \bfseries #2%
|
||||||
|
\markboth{}{}\par}%
|
||||||
|
\nobreak
|
||||||
|
\vskip 3ex
|
||||||
|
\@afterheading}
|
||||||
|
\def\@spart#1{%
|
||||||
|
{\parindent \z@ \raggedright
|
||||||
|
\interlinepenalty \@M
|
||||||
|
\normalfont
|
||||||
|
\huge \bfseries #1\par}%
|
||||||
|
\nobreak
|
||||||
|
\vskip 3ex
|
||||||
|
\@afterheading}
|
||||||
|
\newcommand\section{\@startsection {section}{1}{\z@}%
|
||||||
|
{-3.5ex \@plus -1ex \@minus -.2ex}%
|
||||||
|
{2.3ex \@plus.2ex}%
|
||||||
|
{\normalfont\Large\bfseries}}
|
||||||
|
\newcommand\subsection{\@startsection{subsection}{2}{\z@}%
|
||||||
|
{-3.25ex\@plus -1ex \@minus -.2ex}%
|
||||||
|
{1.5ex \@plus .2ex}%
|
||||||
|
{\normalfont\large\bfseries}}
|
||||||
|
\newcommand\subsubsection{\@startsection{subsubsection}{3}{\z@}%
|
||||||
|
{-3.25ex\@plus -1ex \@minus -.2ex}%
|
||||||
|
{1.5ex \@plus .2ex}%
|
||||||
|
{\normalfont\normalsize\bfseries}}
|
||||||
|
\newcommand\paragraph{\@startsection{paragraph}{4}{\z@}%
|
||||||
|
{3.25ex \@plus1ex \@minus.2ex}%
|
||||||
|
{-1em}%
|
||||||
|
{\normalfont\normalsize\bfseries}}
|
||||||
|
\newcommand\subparagraph{\@startsection{subparagraph}{5}{\parindent}%
|
||||||
|
{3.25ex \@plus1ex \@minus .2ex}%
|
||||||
|
{-1em}%
|
||||||
|
{\normalfont\normalsize\bfseries}}
|
||||||
|
\if@twocolumn
|
||||||
|
\setlength\leftmargini {2em}
|
||||||
|
\else
|
||||||
|
\setlength\leftmargini {2.5em}
|
||||||
|
\fi
|
||||||
|
\leftmargin \leftmargini
|
||||||
|
\setlength\leftmarginii {2.2em}
|
||||||
|
\setlength\leftmarginiii {1.87em}
|
||||||
|
\setlength\leftmarginiv {1.7em}
|
||||||
|
\if@twocolumn
|
||||||
|
\setlength\leftmarginv {.5em}
|
||||||
|
\setlength\leftmarginvi {.5em}
|
||||||
|
\else
|
||||||
|
\setlength\leftmarginv {1em}
|
||||||
|
\setlength\leftmarginvi {1em}
|
||||||
|
\fi
|
||||||
|
\setlength \labelsep {.5em}
|
||||||
|
\setlength \labelwidth{\leftmargini}
|
||||||
|
\addtolength\labelwidth{-\labelsep}
|
||||||
|
\@beginparpenalty -\@lowpenalty
|
||||||
|
\@endparpenalty -\@lowpenalty
|
||||||
|
\@itempenalty -\@lowpenalty
|
||||||
|
\renewcommand\theenumi{\@arabic\c@enumi}
|
||||||
|
\renewcommand\theenumii{\@alph\c@enumii}
|
||||||
|
\renewcommand\theenumiii{\@roman\c@enumiii}
|
||||||
|
\renewcommand\theenumiv{\@Alph\c@enumiv}
|
||||||
|
\newcommand\labelenumi{\theenumi.}
|
||||||
|
\newcommand\labelenumii{(\theenumii)}
|
||||||
|
\newcommand\labelenumiii{\theenumiii.}
|
||||||
|
\newcommand\labelenumiv{\theenumiv.}
|
||||||
|
\renewcommand\p@enumii{\theenumi}
|
||||||
|
\renewcommand\p@enumiii{\theenumi(\theenumii)}
|
||||||
|
\renewcommand\p@enumiv{\p@enumiii\theenumiii}
|
||||||
|
\newcommand\labelitemi{\textbullet}
|
||||||
|
\newcommand\labelitemii{\normalfont\bfseries \textendash}
|
||||||
|
\newcommand\labelitemiii{\textasteriskcentered}
|
||||||
|
\newcommand\labelitemiv{\textperiodcentered}
|
||||||
|
\newenvironment{description}
|
||||||
|
{\list{}{\labelwidth\z@ \itemindent-\leftmargin
|
||||||
|
\let\makelabel\descriptionlabel}}
|
||||||
|
{\endlist}
|
||||||
|
\newcommand*\descriptionlabel[1]{\hspace\labelsep
|
||||||
|
\normalfont\bfseries #1}
|
||||||
|
\if@titlepage
|
||||||
|
\newenvironment{abstract}{%
|
||||||
|
\titlepage
|
||||||
|
\null\vfil
|
||||||
|
\@beginparpenalty\@lowpenalty
|
||||||
|
\begin{center}%
|
||||||
|
\bfseries \abstractname
|
||||||
|
\@endparpenalty\@M
|
||||||
|
\end{center}}%
|
||||||
|
{\par\vfil\null\endtitlepage}
|
||||||
|
\else
|
||||||
|
\newenvironment{abstract}{%
|
||||||
|
\if@twocolumn
|
||||||
|
\section*{\abstractname}%
|
||||||
|
\else
|
||||||
|
\small
|
||||||
|
\begin{center}%
|
||||||
|
{\bfseries \abstractname\vspace{-.5em}\vspace{\z@}}%
|
||||||
|
\end{center}%
|
||||||
|
\quotation
|
||||||
|
\fi}
|
||||||
|
{\if@twocolumn\else\endquotation\fi}
|
||||||
|
\fi
|
||||||
|
\newenvironment{verse}
|
||||||
|
{\let\\\@centercr
|
||||||
|
\list{}{\itemsep \z@
|
||||||
|
\itemindent -1.5em%
|
||||||
|
\listparindent\itemindent
|
||||||
|
\rightmargin \leftmargin
|
||||||
|
\advance\leftmargin 1.5em}%
|
||||||
|
\item\relax}
|
||||||
|
{\endlist}
|
||||||
|
\newenvironment{quotation}
|
||||||
|
{\list{}{\listparindent 1.5em%
|
||||||
|
\itemindent \listparindent
|
||||||
|
\rightmargin \leftmargin
|
||||||
|
\parsep \z@ \@plus\p@}%
|
||||||
|
\item\relax}
|
||||||
|
{\endlist}
|
||||||
|
\newenvironment{quote}
|
||||||
|
{\list{}{\rightmargin\leftmargin}%
|
||||||
|
\item\relax}
|
||||||
|
{\endlist}
|
||||||
|
\if@compatibility
|
||||||
|
\newenvironment{titlepage}
|
||||||
|
{%
|
||||||
|
\if@twocolumn
|
||||||
|
\@restonecoltrue\onecolumn
|
||||||
|
\else
|
||||||
|
\@restonecolfalse\newpage
|
||||||
|
\fi
|
||||||
|
\thispagestyle{empty}%
|
||||||
|
\setcounter{page}\z@
|
||||||
|
}%
|
||||||
|
{\if@restonecol\twocolumn \else \newpage \fi
|
||||||
|
}
|
||||||
|
\else
|
||||||
|
\newenvironment{titlepage}
|
||||||
|
{%
|
||||||
|
\if@twocolumn
|
||||||
|
\@restonecoltrue\onecolumn
|
||||||
|
\else
|
||||||
|
\@restonecolfalse\newpage
|
||||||
|
\fi
|
||||||
|
\thispagestyle{empty}%
|
||||||
|
\setcounter{page}\@ne
|
||||||
|
}%
|
||||||
|
{\if@restonecol\twocolumn \else \newpage \fi
|
||||||
|
\if@twoside\else
|
||||||
|
\setcounter{page}\@ne
|
||||||
|
\fi
|
||||||
|
}
|
||||||
|
\fi
|
||||||
|
\newcommand\appendix{\par
|
||||||
|
\setcounter{section}{0}%
|
||||||
|
\setcounter{subsection}{0}%
|
||||||
|
\gdef\thesection{\@Alph\c@section}}
|
||||||
|
\setlength\arraycolsep{5\p@}
|
||||||
|
\setlength\tabcolsep{6\p@}
|
||||||
|
\setlength\arrayrulewidth{.4\p@}
|
||||||
|
\setlength\doublerulesep{2\p@}
|
||||||
|
\setlength\tabbingsep{\labelsep}
|
||||||
|
\skip\@mpfootins = \skip\footins
|
||||||
|
\setlength\fboxsep{3\p@}
|
||||||
|
\setlength\fboxrule{.4\p@}
|
||||||
|
\renewcommand \theequation {\@arabic\c@equation}
|
||||||
|
\newcounter{figure}
|
||||||
|
\renewcommand \thefigure {\@arabic\c@figure}
|
||||||
|
\def\fps@figure{tbp}
|
||||||
|
\def\ftype@figure{1}
|
||||||
|
\def\ext@figure{lof}
|
||||||
|
\def\fnum@figure{\figurename\nobreakspace\thefigure}
|
||||||
|
\newenvironment{figure}
|
||||||
|
{\@float{figure}}
|
||||||
|
{\end@float}
|
||||||
|
\newenvironment{figure*}
|
||||||
|
{\@dblfloat{figure}}
|
||||||
|
{\end@dblfloat}
|
||||||
|
\newcounter{table}
|
||||||
|
\renewcommand\thetable{\@arabic\c@table}
|
||||||
|
\def\fps@table{tbp}
|
||||||
|
\def\ftype@table{2}
|
||||||
|
\def\ext@table{lot}
|
||||||
|
\def\fnum@table{\tablename\nobreakspace\thetable}
|
||||||
|
\newenvironment{table}
|
||||||
|
{\@float{table}}
|
||||||
|
{\end@float}
|
||||||
|
\newenvironment{table*}
|
||||||
|
{\@dblfloat{table}}
|
||||||
|
{\end@dblfloat}
|
||||||
|
\newlength\abovecaptionskip
|
||||||
|
\newlength\belowcaptionskip
|
||||||
|
\setlength\abovecaptionskip{10\p@}
|
||||||
|
\setlength\belowcaptionskip{0\p@}
|
||||||
|
\long\def\@makecaption#1#2{%
|
||||||
|
\vskip\abovecaptionskip
|
||||||
|
\sbox\@tempboxa{#1: #2}%
|
||||||
|
\ifdim \wd\@tempboxa >\hsize
|
||||||
|
#1: #2\par
|
||||||
|
\else
|
||||||
|
\global \@minipagefalse
|
||||||
|
\hb@xt@\hsize{\hfil\box\@tempboxa\hfil}%
|
||||||
|
\fi
|
||||||
|
\vskip\belowcaptionskip}
|
||||||
|
\DeclareOldFontCommand{\rm}{\normalfont\rmfamily}{\mathrm}
|
||||||
|
\DeclareOldFontCommand{\sf}{\normalfont\sffamily}{\mathsf}
|
||||||
|
\DeclareOldFontCommand{\tt}{\normalfont\ttfamily}{\mathtt}
|
||||||
|
\DeclareOldFontCommand{\bf}{\normalfont\bfseries}{\mathbf}
|
||||||
|
\DeclareOldFontCommand{\it}{\normalfont\itshape}{\mathit}
|
||||||
|
\DeclareOldFontCommand{\sl}{\normalfont\slshape}{\@nomath\sl}
|
||||||
|
\DeclareOldFontCommand{\sc}{\normalfont\scshape}{\@nomath\sc}
|
||||||
|
\DeclareRobustCommand*\cal{\@fontswitch\relax\mathcal}
|
||||||
|
\DeclareRobustCommand*\mit{\@fontswitch\relax\mathnormal}
|
||||||
|
\newcommand\@pnumwidth{1.55em}
|
||||||
|
\newcommand\@tocrmarg{2.55em}
|
||||||
|
\newcommand\@dotsep{4.5}
|
||||||
|
\setcounter{tocdepth}{3}
|
||||||
|
\newcommand\tableofcontents{%
|
||||||
|
\section*{\contentsname
|
||||||
|
\@mkboth{%
|
||||||
|
\MakeUppercase\contentsname}{\MakeUppercase\contentsname}}%
|
||||||
|
\@starttoc{toc}%
|
||||||
|
}
|
||||||
|
\newcommand*\l@part[2]{%
|
||||||
|
\ifnum \c@tocdepth >-2\relax
|
||||||
|
\addpenalty\@secpenalty
|
||||||
|
\addvspace{2.25em \@plus\p@}%
|
||||||
|
\setlength\@tempdima{3em}%
|
||||||
|
\begingroup
|
||||||
|
\parindent \z@ \rightskip \@pnumwidth
|
||||||
|
\parfillskip -\@pnumwidth
|
||||||
|
{\leavevmode
|
||||||
|
\large \bfseries #1\hfil \hb@xt@\@pnumwidth{\hss #2}}\par
|
||||||
|
\nobreak
|
||||||
|
\if@compatibility
|
||||||
|
\global\@nobreaktrue
|
||||||
|
\everypar{\global\@nobreakfalse\everypar{}}%
|
||||||
|
\fi
|
||||||
|
\endgroup
|
||||||
|
\fi}
|
||||||
|
\newcommand*\l@section[2]{%
|
||||||
|
\ifnum \c@tocdepth >\z@
|
||||||
|
\addpenalty\@secpenalty
|
||||||
|
\addvspace{1.0em \@plus\p@}%
|
||||||
|
\setlength\@tempdima{1.5em}%
|
||||||
|
\begingroup
|
||||||
|
\parindent \z@ \rightskip \@pnumwidth
|
||||||
|
\parfillskip -\@pnumwidth
|
||||||
|
\leavevmode \bfseries
|
||||||
|
\advance\leftskip\@tempdima
|
||||||
|
\hskip -\leftskip
|
||||||
|
#1\nobreak\hfil \nobreak\hb@xt@\@pnumwidth{\hss #2}\par
|
||||||
|
\endgroup
|
||||||
|
\fi}
|
||||||
|
\newcommand*\l@subsection{\@dottedtocline{2}{1.5em}{2.3em}}
|
||||||
|
\newcommand*\l@subsubsection{\@dottedtocline{3}{3.8em}{3.2em}}
|
||||||
|
\newcommand*\l@paragraph{\@dottedtocline{4}{7.0em}{4.1em}}
|
||||||
|
\newcommand*\l@subparagraph{\@dottedtocline{5}{10em}{5em}}
|
||||||
|
\newcommand\listoffigures{%
|
||||||
|
\section*{\listfigurename}%
|
||||||
|
\@mkboth{\MakeUppercase\listfigurename}%
|
||||||
|
{\MakeUppercase\listfigurename}%
|
||||||
|
\@starttoc{lof}%
|
||||||
|
}
|
||||||
|
\newcommand*\l@figure{\@dottedtocline{1}{1.5em}{2.3em}}
|
||||||
|
\newcommand\listoftables{%
|
||||||
|
\section*{\listtablename}%
|
||||||
|
\@mkboth{%
|
||||||
|
\MakeUppercase\listtablename}%
|
||||||
|
{\MakeUppercase\listtablename}%
|
||||||
|
\@starttoc{lot}%
|
||||||
|
}
|
||||||
|
\let\l@table\l@figure
|
||||||
|
\newdimen\bibindent
|
||||||
|
\setlength\bibindent{1.5em}
|
||||||
|
\newenvironment{thebibliography}[1]
|
||||||
|
{\section*{\refname}%
|
||||||
|
\@mkboth{\MakeUppercase\refname}{\MakeUppercase\refname}%
|
||||||
|
\list{\@biblabel{\@arabic\c@enumiv}}%
|
||||||
|
{\settowidth\labelwidth{\@biblabel{#1}}%
|
||||||
|
\leftmargin\labelwidth
|
||||||
|
\advance\leftmargin\labelsep
|
||||||
|
\@openbib@code
|
||||||
|
\usecounter{enumiv}%
|
||||||
|
\let\p@enumiv\@empty
|
||||||
|
\renewcommand\theenumiv{\@arabic\c@enumiv}}%
|
||||||
|
\sloppy
|
||||||
|
\clubpenalty4000
|
||||||
|
\@clubpenalty \clubpenalty
|
||||||
|
\widowpenalty4000%
|
||||||
|
\sfcode`\.\@m}
|
||||||
|
{\def\@noitemerr
|
||||||
|
{\@latex@warning{Empty `thebibliography' environment}}%
|
||||||
|
\endlist}
|
||||||
|
\newcommand\newblock{\hskip .11em\@plus.33em\@minus.07em}
|
||||||
|
\let\@openbib@code\@empty
|
||||||
|
\newenvironment{theindex}
|
||||||
|
{\if@twocolumn
|
||||||
|
\@restonecolfalse
|
||||||
|
\else
|
||||||
|
\@restonecoltrue
|
||||||
|
\fi
|
||||||
|
\twocolumn[\section*{\indexname}]%
|
||||||
|
\@mkboth{\MakeUppercase\indexname}%
|
||||||
|
{\MakeUppercase\indexname}%
|
||||||
|
\thispagestyle{plain}\parindent\z@
|
||||||
|
\parskip\z@ \@plus .3\p@\relax
|
||||||
|
\columnseprule \z@
|
||||||
|
\columnsep 35\p@
|
||||||
|
\let\item\@idxitem}
|
||||||
|
{\if@restonecol\onecolumn\else\clearpage\fi}
|
||||||
|
\newcommand\@idxitem{\par\hangindent 40\p@}
|
||||||
|
\newcommand\subitem{\@idxitem \hspace*{20\p@}}
|
||||||
|
\newcommand\subsubitem{\@idxitem \hspace*{30\p@}}
|
||||||
|
\newcommand\indexspace{\par \vskip 10\p@ \@plus5\p@ \@minus3\p@\relax}
|
||||||
|
\renewcommand\footnoterule{%
|
||||||
|
\kern-3\p@
|
||||||
|
\hrule\@width.4\columnwidth
|
||||||
|
\kern2.6\p@}
|
||||||
|
\newcommand\@makefntext[1]{%
|
||||||
|
\parindent 1em%
|
||||||
|
\noindent
|
||||||
|
\hb@xt@1.8em{\hss\@makefnmark}#1}
|
||||||
|
\newcommand\contentsname{Contents}
|
||||||
|
\newcommand\listfigurename{List of Figures}
|
||||||
|
\newcommand\listtablename{List of Tables}
|
||||||
|
\newcommand\refname{References}
|
||||||
|
\newcommand\indexname{Index}
|
||||||
|
\newcommand\figurename{Figure}
|
||||||
|
\newcommand\tablename{Table}
|
||||||
|
\newcommand\partname{Part}
|
||||||
|
\newcommand\appendixname{Appendix}
|
||||||
|
\newcommand\abstractname{}
|
||||||
|
\def\today{\ifcase\month\or
|
||||||
|
January\or February\or March\or April\or May\or June\or
|
||||||
|
July\or August\or September\or October\or November\or December\fi
|
||||||
|
\space\number\day, \number\year}
|
||||||
|
\setlength\columnsep{10\p@}
|
||||||
|
\setlength\columnseprule{0\p@}
|
||||||
|
\pagestyle{plain}
|
||||||
|
\pagenumbering{arabic}
|
||||||
|
|
||||||
|
% === ARLIMS adaptions following ============================================
|
||||||
|
\pagestyle{empty}
|
||||||
|
|
||||||
|
\setlength{\oddsidemargin}{1.5 cm}
|
||||||
|
\setlength{\evensidemargin}{-0.5 cm}
|
||||||
|
\setlength{\textheight}{22 cm}
|
||||||
|
\setlength{\textwidth}{15 cm}
|
||||||
|
\setlength{\topmargin}{0 cm}
|
||||||
|
|
||||||
|
% --- special indexing to cope with the list of articles
|
||||||
|
\newif\ifIndexMade
|
||||||
|
\def\makeartIndex{%
|
||||||
|
\protected@write1{}{\string\global\string\IndexMadetrue}
|
||||||
|
\newwrite\@artIndexfile
|
||||||
|
\immediate\openout\@artIndexfile=\jobname.adx
|
||||||
|
\def\artIndex{\@bsphack\begingroup\@sanitize\@wrartIndex}
|
||||||
|
\typeout{Writing artIndex file \jobname.adx}%
|
||||||
|
\let\makeartIndex\@empty
|
||||||
|
}
|
||||||
|
|
||||||
|
\def\@wrartIndex#1{%
|
||||||
|
\protected@write\@artIndexfile{}{%
|
||||||
|
\string\artIndexentry{\factTitle}%
|
||||||
|
{\thepage}%
|
||||||
|
{\string\last#1}%
|
||||||
|
{\factAuthor\factAuthorB\factAuthorC\factAuthorD}}%
|
||||||
|
\endgroup%
|
||||||
|
\@esphack%
|
||||||
|
}
|
||||||
|
|
||||||
|
\def\artIndex{\@bsphack\begingroup \@sanitize\@artIndex}
|
||||||
|
\def\@artIndex#1{\endgroup\@esphack}
|
||||||
|
|
||||||
|
\def\artIndexLast#1{%
|
||||||
|
\protected@write1{}{\string\gdef\string\last#1{\thepage}}%
|
||||||
|
}
|
||||||
|
|
||||||
|
% --- to set up the headers permanently
|
||||||
|
\newcommand{\markperm}[2]{%
|
||||||
|
\global\def\@evenhead{\arabic{page}\hfil{#1}}%
|
||||||
|
\global\def\@oddhead{{#2}\hfil\arabic{page}}
|
||||||
|
}
|
||||||
|
% (yes, I know I've hardwired arabic page numbering)
|
||||||
|
|
||||||
|
% --- to set up the headers with a once off first
|
||||||
|
\newcommand{\markonce}[3]{%
|
||||||
|
\global\def\@evenhead{\arabic{page}\hfil{#1}\markperm{#2}{#3}}%
|
||||||
|
\global\def\@oddhead{{#1}\hfil\arabic{page}\markperm{#2}{#3}}%
|
||||||
|
}
|
||||||
|
|
||||||
|
%% Put a line after the abstract %%
|
||||||
|
\newcommand{\drawline}{%
|
||||||
|
\vspace*{1mm}
|
||||||
|
\begin{center}
|
||||||
|
\rule{\textwidth}{0.2mm}
|
||||||
|
\end{center}
|
||||||
|
\vspace*{1mm}
|
||||||
|
}
|
||||||
|
|
||||||
|
\newcommand{\reff}[1]{(\ref{#1})}
|
||||||
|
|
||||||
|
|
||||||
|
% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
% Define the author and title commands. Bascially each just stuffs
|
||||||
|
% the argument into a storage command. I need to define the blank
|
||||||
|
% storage commands because I am using the "renewcomand", since I
|
||||||
|
% need to do this for each article.
|
||||||
|
|
||||||
|
\def\author{}
|
||||||
|
\def\institute{}
|
||||||
|
|
||||||
|
\def\factTitle{}
|
||||||
|
|
||||||
|
\def\factAuthor{}
|
||||||
|
\def\factAuthorB{}
|
||||||
|
\def\factAuthorC{}
|
||||||
|
\def\factAuthorD{}
|
||||||
|
|
||||||
|
\def\factInstitute{}
|
||||||
|
\def\factInstituteB{}
|
||||||
|
\def\factInstituteC{}
|
||||||
|
\def\factInstituteD{}
|
||||||
|
|
||||||
|
\def\footInst{}
|
||||||
|
\def\footInstB{}
|
||||||
|
\def\footInstC{}
|
||||||
|
\def\footInstD{}
|
||||||
|
|
||||||
|
\def\footAuthorInst{}
|
||||||
|
\def\footAuthorInstB{}
|
||||||
|
\def\footAuthorInstC{}
|
||||||
|
\def\footAuthorInstD{}
|
||||||
|
|
||||||
|
\renewcommand{\author}[2][]{
|
||||||
|
\renewcommand{\footAuthorInst}{#1}
|
||||||
|
\renewcommand{\factAuthor}{#2}
|
||||||
|
}
|
||||||
|
|
||||||
|
\newcommand{\authorB}[2][]{
|
||||||
|
\renewcommand{\footAuthorInstB}{#1}
|
||||||
|
\renewcommand{\factAuthorB}{#2}
|
||||||
|
}
|
||||||
|
|
||||||
|
\newcommand{\authorC}[2][]{
|
||||||
|
\renewcommand{\footAuthorInstC}{#1}
|
||||||
|
\renewcommand{\factAuthorC}{#2}
|
||||||
|
}
|
||||||
|
|
||||||
|
\newcommand{\authorD}[2][]{
|
||||||
|
\renewcommand{\footAuthorInstD}{#1}
|
||||||
|
\renewcommand{\factAuthorD}{#2}
|
||||||
|
}
|
||||||
|
|
||||||
|
\renewcommand{\institute}[2][]{
|
||||||
|
\renewcommand{\footInst}{#1}
|
||||||
|
\renewcommand{\factInstitute}{#2}
|
||||||
|
}
|
||||||
|
|
||||||
|
\newcommand{\instituteB}[2][*]{
|
||||||
|
\renewcommand{\footInstB}{#1}
|
||||||
|
\renewcommand{\factInstituteB}{#2}
|
||||||
|
}
|
||||||
|
|
||||||
|
\newcommand{\instituteC}[2][**]{
|
||||||
|
\renewcommand{\footInstC}{#1}
|
||||||
|
\renewcommand{\factInstituteC}{#2}
|
||||||
|
}
|
||||||
|
|
||||||
|
\newcommand{\instituteD}[2][***]{
|
||||||
|
\renewcommand{\footInstD}{#1}
|
||||||
|
\renewcommand{\factInstituteD}{#2}
|
||||||
|
}
|
||||||
|
|
||||||
|
\def\nameTag{}
|
||||||
|
|
||||||
|
\renewcommand{\title}[1]{
|
||||||
|
\renewcommand{\factTitle}{#1}
|
||||||
|
}
|
||||||
|
|
||||||
|
\newcommand{\iimsaddress}{
|
||||||
|
\begin{center}
|
||||||
|
\textit{Institute of Information \& Mathematical Sciences\\
|
||||||
|
Massey University at Albany, Auckland, New Zealand.}
|
||||||
|
\end{center}
|
||||||
|
}
|
||||||
|
|
||||||
|
\newcommand{\maketitle}[1][\factTitle]{
|
||||||
|
\begin{center}
|
||||||
|
\markonce{
|
||||||
|
\parbox{10cm}{\small
|
||||||
|
\textit{The Python Papers Monograph,} Vol.~1 (2009)\\[1mm]
|
||||||
|
Available online at http://ojs.pythonpapers.org/index.php/tppm
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{\factAuthor\factAuthorB\factAuthorC\factAuthorD}
|
||||||
|
{#1}
|
||||||
|
|
||||||
|
\vspace*{1cm}
|
||||||
|
|
||||||
|
\begin{minipage}{12cm}
|
||||||
|
\begin{center}
|
||||||
|
{\bfseries\Large\factTitle\par}
|
||||||
|
\vspace*{2mm}
|
||||||
|
\end{center}
|
||||||
|
\end{minipage}
|
||||||
|
|
||||||
|
\vspace*{3mm}
|
||||||
|
|
||||||
|
\textsc{%
|
||||||
|
\factAuthor $^{\footAuthorInst}$%
|
||||||
|
\factAuthorB $^{\footAuthorInstB}$%
|
||||||
|
\factAuthorC $^{\footAuthorInstC}$%
|
||||||
|
\factAuthorD $^{\footAuthorInstD}$%
|
||||||
|
}
|
||||||
|
|
||||||
|
\vspace*{1mm}
|
||||||
|
|
||||||
|
$^{\footInst}$\textit{\factInstitute}
|
||||||
|
$^{\footInstB}$\textit{\factInstituteB}
|
||||||
|
$^{\footInstC}$\textit{\factInstituteC}
|
||||||
|
$^{\footInstD}$\textit{\factInstituteD}
|
||||||
|
|
||||||
|
\artIndex{\nameTag}
|
||||||
|
\end{center}
|
||||||
|
}
|
||||||
|
|
||||||
|
% calculate the margins so that the text on opposite sides
|
||||||
|
% of the same sheet is lined up at the edges
|
||||||
|
% ok, its a fudge, but you can change the oddsidemargin
|
||||||
|
% to shift both sides in sync.
|
||||||
|
|
||||||
|
\oddsidemargin1cm
|
||||||
|
\evensidemargin\paperwidth
|
||||||
|
\addtolength{\evensidemargin}{-\oddsidemargin}
|
||||||
|
\addtolength{\evensidemargin}{-205mm}
|
||||||
|
|
||||||
|
% --- turn off a few things -------------------------------------------------
|
||||||
|
\renewcommand{\markboth}[2]{}
|
||||||
|
\renewcommand{\markright}[1]{}
|
||||||
|
\newcommand{\markheading}[1]{}
|
||||||
|
|
||||||
|
\usepackage{amsmath,amsfonts,dsfont}
|
||||||
|
\usepackage{amsthm}
|
||||||
|
|
||||||
|
\newif\ifCombinedRlims
|
||||||
|
|
||||||
|
% === end of ARLIMS adaptions ===============================================
|
||||||
|
|
||||||
|
\if@twoside
|
||||||
|
\else
|
||||||
|
\raggedbottom
|
||||||
|
\fi
|
||||||
|
\if@twocolumn
|
||||||
|
\twocolumn
|
||||||
|
\sloppy
|
||||||
|
\flushbottom
|
||||||
|
\else
|
||||||
|
\onecolumn
|
||||||
|
\fi
|
||||||
|
\endinput
|
||||||
|
%%
|
||||||
|
%% End of file `arlims.cls'.
|
||||||
898
doc/KPyCon2009/arlimsTPPSC.cls
Normal file
@@ -0,0 +1,898 @@
|
|||||||
|
%%
|
||||||
|
%% This is file `arlims.cls',
|
||||||
|
%% It is based on the original `article.cls' from a tetex 3.0
|
||||||
|
%% distribution (Ubuntu tetex-base 3.0-19 package).
|
||||||
|
%%
|
||||||
|
%% Additions to it were made by (probably) various authors at the
|
||||||
|
%% Massey University Institute of Information and Mathematical
|
||||||
|
%% Sciences.
|
||||||
|
%%
|
||||||
|
%% For further information contact Heath James
|
||||||
|
%% <h.a.james@massey.ac.nz>, Guy Kloss <g.kloss@massey.ac.nz>, Paul
|
||||||
|
%% Cowpertwait <p.s.cowpertwait@massey.ac.nz> or anybody you expect
|
||||||
|
%% suitable for this purpose ...
|
||||||
|
%%
|
||||||
|
%% Copyright 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004
|
||||||
|
%% The LaTeX3 Project and any individual authors listed elsewhere
|
||||||
|
%% in this file.
|
||||||
|
%%
|
||||||
|
%% This file was generated from file(s) of the LaTeX base system.
|
||||||
|
%% Numerous changes were made to be fit for RLIMS.
|
||||||
|
%% --------------------------------------------------------------
|
||||||
|
%%
|
||||||
|
%% Changes:
|
||||||
|
%%
|
||||||
|
%% * numerous changes previously to fit article.sty for the purpose
|
||||||
|
%%
|
||||||
|
%% version 1.5:
|
||||||
|
%%
|
||||||
|
%% * fixed the instructions
|
||||||
|
%% * added better default options to the template
|
||||||
|
%% * changed the year to 2008
|
||||||
|
%%
|
||||||
|
%% version 1.4:
|
||||||
|
%%
|
||||||
|
%% * fitted to a newer version of article.sty
|
||||||
|
%% * cleanups in layout and formatting
|
||||||
|
%% * better compliance to LaTeX2e
|
||||||
|
%% * re-fitted \institute, \instituteB, ...C, ...D
|
||||||
|
%% it doesn't work 100% nicely, but it works)
|
||||||
|
%% * removed dependency on natbib.sty (so cite.sty, or else can be
|
||||||
|
%% used as liked)
|
||||||
|
%% * changed paper size to a4paper by default
|
||||||
|
%% * removed dependencies to graphicx.sty and subfigure.sty
|
||||||
|
%% (users like to choose and do that themselves)
|
||||||
|
%%
|
||||||
|
%% --------------------------------------------------------------
|
||||||
|
%%
|
||||||
|
%% It may be distributed and/or modified under the
|
||||||
|
%% conditions of the LaTeX Project Public License, either version 1.3
|
||||||
|
%% of this license or (at your option) any later version.
|
||||||
|
%% The latest version of this license is in
|
||||||
|
%% http://www.latex-project.org/lppl.txt
|
||||||
|
%% and version 1.3 or later is part of all distributions of LaTeX
|
||||||
|
%% version 2003/12/01 or later.
|
||||||
|
%%
|
||||||
|
%% This file has the LPPL maintenance status "maintained".
|
||||||
|
%%
|
||||||
|
%% This file may only be distributed together with a copy of the LaTeX
|
||||||
|
%% base system. You may however distribute the LaTeX base system without
|
||||||
|
%% such generated files.
|
||||||
|
%%
|
||||||
|
%% The list of all files belonging to the LaTeX base distribution is
|
||||||
|
%% given in the file `manifest.txt'. See also `legal.txt' for additional
|
||||||
|
%% information.
|
||||||
|
%%
|
||||||
|
%% The list of derived (unpacked) files belonging to the distribution
|
||||||
|
%% and covered by LPPL is defined by the unpacking scripts (with
|
||||||
|
%% extension .ins) which are part of the distribution.
|
||||||
|
%% \CharacterTable
|
||||||
|
%% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z
|
||||||
|
%% Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z
|
||||||
|
%% Digits \0\1\2"s\4\5\6\7\8\9
|
||||||
|
%% Exclamation \! Double quote \" Hash (number) \#
|
||||||
|
%% Dollar \$ Percent \% Ampersand \&
|
||||||
|
%% Acute accent \' Left paren \( Right paren \)
|
||||||
|
%% Asterisk \* Plus \+ Comma \,
|
||||||
|
%% Minus \- Point \. Solidus \/
|
||||||
|
%% Colon \: Semicolon \; Less than \<
|
||||||
|
%% Equals \= Greater than \> Question mark \?
|
||||||
|
%% Commercial at \@ Left bracket \[ Backslash \\
|
||||||
|
%% Right bracket \] Circumflex \^ Underscore \_
|
||||||
|
%% Grave accent \` Left brace \{ Vertical bar \|
|
||||||
|
%% Right brace \} Tilde \~}
|
||||||
|
\NeedsTeXFormat{LaTeX2e}[1995/12/01]
|
||||||
|
\ProvidesClass{arlims}
|
||||||
|
[2008/04/23 v1.5
|
||||||
|
Massey Research Letters Article Class]
|
||||||
|
\newcommand\@ptsize{}
|
||||||
|
\newif\if@restonecol
|
||||||
|
\newif\if@titlepage
|
||||||
|
\@titlepagefalse
|
||||||
|
\if@compatibility\else
|
||||||
|
\DeclareOption{a4paper}
|
||||||
|
{\setlength\paperheight {297mm}%
|
||||||
|
\setlength\paperwidth {210mm}}
|
||||||
|
\DeclareOption{a5paper}
|
||||||
|
{\setlength\paperheight {210mm}%
|
||||||
|
\setlength\paperwidth {148mm}}
|
||||||
|
\DeclareOption{b5paper}
|
||||||
|
{\setlength\paperheight {250mm}%
|
||||||
|
\setlength\paperwidth {176mm}}
|
||||||
|
\DeclareOption{letterpaper}
|
||||||
|
{\setlength\paperheight {11in}%
|
||||||
|
\setlength\paperwidth {8.5in}}
|
||||||
|
\DeclareOption{legalpaper}
|
||||||
|
{\setlength\paperheight {14in}%
|
||||||
|
\setlength\paperwidth {8.5in}}
|
||||||
|
\DeclareOption{executivepaper}
|
||||||
|
{\setlength\paperheight {10.5in}%
|
||||||
|
\setlength\paperwidth {7.25in}}
|
||||||
|
\DeclareOption{landscape}
|
||||||
|
{\setlength\@tempdima {\paperheight}%
|
||||||
|
\setlength\paperheight {\paperwidth}%
|
||||||
|
\setlength\paperwidth {\@tempdima}}
|
||||||
|
\fi
|
||||||
|
\if@compatibility
|
||||||
|
\renewcommand\@ptsize{0}
|
||||||
|
\else
|
||||||
|
\DeclareOption{10pt}{\renewcommand\@ptsize{0}}
|
||||||
|
\fi
|
||||||
|
\DeclareOption{11pt}{\renewcommand\@ptsize{1}}
|
||||||
|
\DeclareOption{12pt}{\renewcommand\@ptsize{2}}
|
||||||
|
\if@compatibility\else
|
||||||
|
\DeclareOption{oneside}{\@twosidefalse \@mparswitchfalse}
|
||||||
|
\fi
|
||||||
|
\DeclareOption{twoside}{\@twosidetrue \@mparswitchtrue}
|
||||||
|
\DeclareOption{draft}{\setlength\overfullrule{5pt}}
|
||||||
|
\if@compatibility\else
|
||||||
|
\DeclareOption{final}{\setlength\overfullrule{0pt}}
|
||||||
|
\fi
|
||||||
|
\DeclareOption{titlepage}{\@titlepagetrue}
|
||||||
|
\if@compatibility\else
|
||||||
|
\DeclareOption{notitlepage}{\@titlepagefalse}
|
||||||
|
\fi
|
||||||
|
\if@compatibility\else
|
||||||
|
\DeclareOption{onecolumn}{\@twocolumnfalse}
|
||||||
|
\fi
|
||||||
|
\DeclareOption{twocolumn}{\@twocolumntrue}
|
||||||
|
\DeclareOption{leqno}{\input{leqno.clo}}
|
||||||
|
\DeclareOption{fleqn}{\input{fleqn.clo}}
|
||||||
|
\DeclareOption{openbib}{%
|
||||||
|
\AtEndOfPackage{%
|
||||||
|
\renewcommand\@openbib@code{%
|
||||||
|
\advance\leftmargin\bibindent
|
||||||
|
\itemindent -\bibindent
|
||||||
|
\listparindent \itemindent
|
||||||
|
\parsep \z@
|
||||||
|
}%
|
||||||
|
\renewcommand\newblock{\par}}%
|
||||||
|
}
|
||||||
|
\ExecuteOptions{a4paper,12pt,oneside,onecolumn,final}
|
||||||
|
\ProcessOptions
|
||||||
|
\input{size1\@ptsize.clo}
|
||||||
|
\setlength\lineskip{1\p@}
|
||||||
|
\setlength\normallineskip{1\p@}
|
||||||
|
\renewcommand\baselinestretch{}
|
||||||
|
\setlength\parskip{0\p@ \@plus \p@}
|
||||||
|
\@lowpenalty 51
|
||||||
|
\@medpenalty 151
|
||||||
|
\@highpenalty 301
|
||||||
|
\setcounter{topnumber}{2}
|
||||||
|
\renewcommand\topfraction{.7}
|
||||||
|
\setcounter{bottomnumber}{1}
|
||||||
|
\renewcommand\bottomfraction{.3}
|
||||||
|
\setcounter{totalnumber}{3}
|
||||||
|
\renewcommand\textfraction{.2}
|
||||||
|
\renewcommand\floatpagefraction{.5}
|
||||||
|
\setcounter{dbltopnumber}{2}
|
||||||
|
\renewcommand\dbltopfraction{.7}
|
||||||
|
\renewcommand\dblfloatpagefraction{.5}
|
||||||
|
\if@twoside
|
||||||
|
\def\ps@headings{%
|
||||||
|
\let\@oddfoot\@empty\let\@evenfoot\@empty
|
||||||
|
\def\@evenhead{\thepage\hfil\slshape\leftmark}%
|
||||||
|
\def\@oddhead{{\slshape\rightmark}\hfil\thepage}%
|
||||||
|
\let\@mkboth\markboth
|
||||||
|
\def\sectionmark##1{%
|
||||||
|
\markboth {\MakeUppercase{%
|
||||||
|
\ifnum \c@secnumdepth >\z@
|
||||||
|
\thesection\quad
|
||||||
|
\fi
|
||||||
|
##1}}{}}%
|
||||||
|
\def\subsectionmark##1{%
|
||||||
|
\markright {%
|
||||||
|
\ifnum \c@secnumdepth >\@ne
|
||||||
|
\thesubsection\quad
|
||||||
|
\fi
|
||||||
|
##1}}}
|
||||||
|
\else
|
||||||
|
\def\ps@headings{%
|
||||||
|
\let\@oddfoot\@empty
|
||||||
|
\def\@oddhead{{\slshape\rightmark}\hfil\thepage}%
|
||||||
|
\let\@mkboth\markboth
|
||||||
|
\def\sectionmark##1{%
|
||||||
|
\markright {\MakeUppercase{%
|
||||||
|
\ifnum \c@secnumdepth >\m@ne
|
||||||
|
\thesection\quad
|
||||||
|
\fi
|
||||||
|
##1}}}}
|
||||||
|
\fi
|
||||||
|
\def\ps@myheadings{%
|
||||||
|
\let\@oddfoot\@empty\let\@evenfoot\@empty
|
||||||
|
\def\@evenhead{\thepage\hfil\slshape\leftmark}%
|
||||||
|
\def\@oddhead{{\slshape\rightmark}\hfil\thepage}%
|
||||||
|
\let\@mkboth\@gobbletwo
|
||||||
|
\let\sectionmark\@gobble
|
||||||
|
\let\subsectionmark\@gobble
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
%---------- old definition of title page -------------------------------
|
||||||
|
\newif\ifGrxlb\ifGrxlb
|
||||||
|
|
||||||
|
\if@titlepage
|
||||||
|
\newcommand\maketitle{\begin{titlepage}%
|
||||||
|
\let\footnotesize\small
|
||||||
|
\let\footnoterule\relax
|
||||||
|
\let \footnote \thanks
|
||||||
|
\null\vfil
|
||||||
|
\vskip 60\p@
|
||||||
|
\begin{center}%
|
||||||
|
{\LARGE \@title \par}%
|
||||||
|
\vskip 3em%
|
||||||
|
{\large
|
||||||
|
\lineskip .75em%
|
||||||
|
\begin{tabular}[t]{c}%
|
||||||
|
\@author
|
||||||
|
\end{tabular}\par}%
|
||||||
|
\vskip 1.5em%
|
||||||
|
{\large \@date \par}% % Set date in \large size.
|
||||||
|
\end{center}\par
|
||||||
|
\@thanks
|
||||||
|
\vfil\null
|
||||||
|
\end{titlepage}%
|
||||||
|
\setcounter{footnote}{0}%
|
||||||
|
\global\let\thanks\relax
|
||||||
|
\global\let\maketitle\relax
|
||||||
|
\global\let\@thanks\@empty
|
||||||
|
\global\let\@author\@empty
|
||||||
|
\global\let\@date\@empty
|
||||||
|
\global\let\@title\@empty
|
||||||
|
\global\let\title\relax
|
||||||
|
\global\let\author\relax
|
||||||
|
\global\let\date\relax
|
||||||
|
\global\let\and\relax
|
||||||
|
}
|
||||||
|
\else
|
||||||
|
\newcommand\maketitle{\par
|
||||||
|
\begingroup
|
||||||
|
\renewcommand\thefootnote{\@fnsymbol\c@footnote}%
|
||||||
|
\def\@makefnmark{\rlap{\@textsuperscript{\normalfont\@thefnmark}}}%
|
||||||
|
\long\def\@makefntext##1{\parindent 1em\noindent
|
||||||
|
\hb@xt@1.8em{%
|
||||||
|
\hss\@textsuperscript{\normalfont\@thefnmark}}##1}%
|
||||||
|
\if@twocolumn
|
||||||
|
\ifnum \col@number=\@ne
|
||||||
|
\@maketitle
|
||||||
|
\else
|
||||||
|
\twocolumn[\@maketitle]%
|
||||||
|
\fi
|
||||||
|
\else
|
||||||
|
\newpage
|
||||||
|
\global\@topnum\z@ % Prevents figures from going at top of page.
|
||||||
|
\@maketitle
|
||||||
|
\fi
|
||||||
|
\thispagestyle{plain}\@thanks
|
||||||
|
\endgroup
|
||||||
|
\setcounter{footnote}{0}%
|
||||||
|
\global\let\thanks\relax
|
||||||
|
\global\let\maketitle\relax
|
||||||
|
\global\let\@maketitle\relax
|
||||||
|
\global\let\@thanks\@empty
|
||||||
|
\global\let\@author\@empty
|
||||||
|
\global\let\@date\@empty
|
||||||
|
\global\let\@title\@empty
|
||||||
|
\global\let\title\relax
|
||||||
|
\global\let\author\relax
|
||||||
|
\global\let\date\relax
|
||||||
|
\global\let\and\relax
|
||||||
|
}
|
||||||
|
\def\@maketitle{%
|
||||||
|
\newpage
|
||||||
|
\null
|
||||||
|
\vskip 2em%
|
||||||
|
\begin{center}%
|
||||||
|
\let \footnote \thanks
|
||||||
|
{\LARGE \@title \par}%
|
||||||
|
\vskip 1.5em%
|
||||||
|
{\large
|
||||||
|
\lineskip .5em%
|
||||||
|
\begin{tabular}[t]{c}%
|
||||||
|
\@author
|
||||||
|
\end{tabular}\par}%
|
||||||
|
\vskip 1em%
|
||||||
|
{\large \@date}%
|
||||||
|
\end{center}%
|
||||||
|
\par
|
||||||
|
\vskip 1.5em}
|
||||||
|
\fi
|
||||||
|
|
||||||
|
\fi
|
||||||
|
%---------------- end of old definition of title page ---------------------
|
||||||
|
|
||||||
|
\setcounter{secnumdepth}{3}
|
||||||
|
\newcounter {part}
|
||||||
|
\newcounter {section}
|
||||||
|
\newcounter {subsection}[section]
|
||||||
|
\newcounter {subsubsection}[subsection]
|
||||||
|
\newcounter {paragraph}[subsubsection]
|
||||||
|
\newcounter {subparagraph}[paragraph]
|
||||||
|
\renewcommand \thepart {\@Roman\c@part}
|
||||||
|
\renewcommand \thesection {\@arabic\c@section}
|
||||||
|
\renewcommand\thesubsection {\thesection.\@arabic\c@subsection}
|
||||||
|
\renewcommand\thesubsubsection{\thesubsection .\@arabic\c@subsubsection}
|
||||||
|
\renewcommand\theparagraph {\thesubsubsection.\@arabic\c@paragraph}
|
||||||
|
\renewcommand\thesubparagraph {\theparagraph.\@arabic\c@subparagraph}
|
||||||
|
\newcommand\part{%
|
||||||
|
\if@noskipsec \leavevmode \fi
|
||||||
|
\par
|
||||||
|
\addvspace{4ex}%
|
||||||
|
\@afterindentfalse
|
||||||
|
\secdef\@part\@spart}
|
||||||
|
|
||||||
|
\def\@part[#1]#2{%
|
||||||
|
\ifnum \c@secnumdepth >\m@ne
|
||||||
|
\refstepcounter{part}%
|
||||||
|
\addcontentsline{toc}{part}{\thepart\hspace{1em}#1}%
|
||||||
|
\else
|
||||||
|
\addcontentsline{toc}{part}{#1}%
|
||||||
|
\fi
|
||||||
|
{\parindent \z@ \raggedright
|
||||||
|
\interlinepenalty \@M
|
||||||
|
\normalfont
|
||||||
|
\ifnum \c@secnumdepth >\m@ne
|
||||||
|
\Large\bfseries \partname\nobreakspace\thepart
|
||||||
|
\par\nobreak
|
||||||
|
\fi
|
||||||
|
\huge \bfseries #2%
|
||||||
|
\markboth{}{}\par}%
|
||||||
|
\nobreak
|
||||||
|
\vskip 3ex
|
||||||
|
\@afterheading}
|
||||||
|
\def\@spart#1{%
|
||||||
|
{\parindent \z@ \raggedright
|
||||||
|
\interlinepenalty \@M
|
||||||
|
\normalfont
|
||||||
|
\huge \bfseries #1\par}%
|
||||||
|
\nobreak
|
||||||
|
\vskip 3ex
|
||||||
|
\@afterheading}
|
||||||
|
\newcommand\section{\@startsection {section}{1}{\z@}%
|
||||||
|
{-3.5ex \@plus -1ex \@minus -.2ex}%
|
||||||
|
{2.3ex \@plus.2ex}%
|
||||||
|
{\normalfont\Large\bfseries}}
|
||||||
|
\newcommand\subsection{\@startsection{subsection}{2}{\z@}%
|
||||||
|
{-3.25ex\@plus -1ex \@minus -.2ex}%
|
||||||
|
{1.5ex \@plus .2ex}%
|
||||||
|
{\normalfont\large\bfseries}}
|
||||||
|
\newcommand\subsubsection{\@startsection{subsubsection}{3}{\z@}%
|
||||||
|
{-3.25ex\@plus -1ex \@minus -.2ex}%
|
||||||
|
{1.5ex \@plus .2ex}%
|
||||||
|
{\normalfont\normalsize\bfseries}}
|
||||||
|
\newcommand\paragraph{\@startsection{paragraph}{4}{\z@}%
|
||||||
|
{3.25ex \@plus1ex \@minus.2ex}%
|
||||||
|
{-1em}%
|
||||||
|
{\normalfont\normalsize\bfseries}}
|
||||||
|
\newcommand\subparagraph{\@startsection{subparagraph}{5}{\parindent}%
|
||||||
|
{3.25ex \@plus1ex \@minus .2ex}%
|
||||||
|
{-1em}%
|
||||||
|
{\normalfont\normalsize\bfseries}}
|
||||||
|
\if@twocolumn
|
||||||
|
\setlength\leftmargini {2em}
|
||||||
|
\else
|
||||||
|
\setlength\leftmargini {2.5em}
|
||||||
|
\fi
|
||||||
|
\leftmargin \leftmargini
|
||||||
|
\setlength\leftmarginii {2.2em}
|
||||||
|
\setlength\leftmarginiii {1.87em}
|
||||||
|
\setlength\leftmarginiv {1.7em}
|
||||||
|
\if@twocolumn
|
||||||
|
\setlength\leftmarginv {.5em}
|
||||||
|
\setlength\leftmarginvi {.5em}
|
||||||
|
\else
|
||||||
|
\setlength\leftmarginv {1em}
|
||||||
|
\setlength\leftmarginvi {1em}
|
||||||
|
\fi
|
||||||
|
\setlength \labelsep {.5em}
|
||||||
|
\setlength \labelwidth{\leftmargini}
|
||||||
|
\addtolength\labelwidth{-\labelsep}
|
||||||
|
\@beginparpenalty -\@lowpenalty
|
||||||
|
\@endparpenalty -\@lowpenalty
|
||||||
|
\@itempenalty -\@lowpenalty
|
||||||
|
\renewcommand\theenumi{\@arabic\c@enumi}
|
||||||
|
\renewcommand\theenumii{\@alph\c@enumii}
|
||||||
|
\renewcommand\theenumiii{\@roman\c@enumiii}
|
||||||
|
\renewcommand\theenumiv{\@Alph\c@enumiv}
|
||||||
|
\newcommand\labelenumi{\theenumi.}
|
||||||
|
\newcommand\labelenumii{(\theenumii)}
|
||||||
|
\newcommand\labelenumiii{\theenumiii.}
|
||||||
|
\newcommand\labelenumiv{\theenumiv.}
|
||||||
|
\renewcommand\p@enumii{\theenumi}
|
||||||
|
\renewcommand\p@enumiii{\theenumi(\theenumii)}
|
||||||
|
\renewcommand\p@enumiv{\p@enumiii\theenumiii}
|
||||||
|
\newcommand\labelitemi{\textbullet}
|
||||||
|
\newcommand\labelitemii{\normalfont\bfseries \textendash}
|
||||||
|
\newcommand\labelitemiii{\textasteriskcentered}
|
||||||
|
\newcommand\labelitemiv{\textperiodcentered}
|
||||||
|
\newenvironment{description}
|
||||||
|
{\list{}{\labelwidth\z@ \itemindent-\leftmargin
|
||||||
|
\let\makelabel\descriptionlabel}}
|
||||||
|
{\endlist}
|
||||||
|
\newcommand*\descriptionlabel[1]{\hspace\labelsep
|
||||||
|
\normalfont\bfseries #1}
|
||||||
|
\if@titlepage
|
||||||
|
\newenvironment{abstract}{%
|
||||||
|
\titlepage
|
||||||
|
\null\vfil
|
||||||
|
\@beginparpenalty\@lowpenalty
|
||||||
|
\begin{center}%
|
||||||
|
\bfseries \abstractname
|
||||||
|
\@endparpenalty\@M
|
||||||
|
\end{center}}%
|
||||||
|
{\par\vfil\null\endtitlepage}
|
||||||
|
\else
|
||||||
|
\newenvironment{abstract}{%
|
||||||
|
\if@twocolumn
|
||||||
|
\section*{\abstractname}%
|
||||||
|
\else
|
||||||
|
\small
|
||||||
|
\begin{center}%
|
||||||
|
{\bfseries \abstractname\vspace{-.5em}\vspace{\z@}}%
|
||||||
|
\end{center}%
|
||||||
|
\quotation
|
||||||
|
\fi}
|
||||||
|
{\if@twocolumn\else\endquotation\fi}
|
||||||
|
\fi
|
||||||
|
\newenvironment{verse}
|
||||||
|
{\let\\\@centercr
|
||||||
|
\list{}{\itemsep \z@
|
||||||
|
\itemindent -1.5em%
|
||||||
|
\listparindent\itemindent
|
||||||
|
\rightmargin \leftmargin
|
||||||
|
\advance\leftmargin 1.5em}%
|
||||||
|
\item\relax}
|
||||||
|
{\endlist}
|
||||||
|
\newenvironment{quotation}
|
||||||
|
{\list{}{\listparindent 1.5em%
|
||||||
|
\itemindent \listparindent
|
||||||
|
\rightmargin \leftmargin
|
||||||
|
\parsep \z@ \@plus\p@}%
|
||||||
|
\item\relax}
|
||||||
|
{\endlist}
|
||||||
|
\newenvironment{quote}
|
||||||
|
{\list{}{\rightmargin\leftmargin}%
|
||||||
|
\item\relax}
|
||||||
|
{\endlist}
|
||||||
|
\if@compatibility
|
||||||
|
\newenvironment{titlepage}
|
||||||
|
{%
|
||||||
|
\if@twocolumn
|
||||||
|
\@restonecoltrue\onecolumn
|
||||||
|
\else
|
||||||
|
\@restonecolfalse\newpage
|
||||||
|
\fi
|
||||||
|
\thispagestyle{empty}%
|
||||||
|
\setcounter{page}\z@
|
||||||
|
}%
|
||||||
|
{\if@restonecol\twocolumn \else \newpage \fi
|
||||||
|
}
|
||||||
|
\else
|
||||||
|
\newenvironment{titlepage}
|
||||||
|
{%
|
||||||
|
\if@twocolumn
|
||||||
|
\@restonecoltrue\onecolumn
|
||||||
|
\else
|
||||||
|
\@restonecolfalse\newpage
|
||||||
|
\fi
|
||||||
|
\thispagestyle{empty}%
|
||||||
|
\setcounter{page}\@ne
|
||||||
|
}%
|
||||||
|
{\if@restonecol\twocolumn \else \newpage \fi
|
||||||
|
\if@twoside\else
|
||||||
|
\setcounter{page}\@ne
|
||||||
|
\fi
|
||||||
|
}
|
||||||
|
\fi
|
||||||
|
\newcommand\appendix{\par
|
||||||
|
\setcounter{section}{0}%
|
||||||
|
\setcounter{subsection}{0}%
|
||||||
|
\gdef\thesection{\@Alph\c@section}}
|
||||||
|
\setlength\arraycolsep{5\p@}
|
||||||
|
\setlength\tabcolsep{6\p@}
|
||||||
|
\setlength\arrayrulewidth{.4\p@}
|
||||||
|
\setlength\doublerulesep{2\p@}
|
||||||
|
\setlength\tabbingsep{\labelsep}
|
||||||
|
\skip\@mpfootins = \skip\footins
|
||||||
|
\setlength\fboxsep{3\p@}
|
||||||
|
\setlength\fboxrule{.4\p@}
|
||||||
|
\renewcommand \theequation {\@arabic\c@equation}
|
||||||
|
\newcounter{figure}
|
||||||
|
\renewcommand \thefigure {\@arabic\c@figure}
|
||||||
|
\def\fps@figure{tbp}
|
||||||
|
\def\ftype@figure{1}
|
||||||
|
\def\ext@figure{lof}
|
||||||
|
\def\fnum@figure{\figurename\nobreakspace\thefigure}
|
||||||
|
\newenvironment{figure}
|
||||||
|
{\@float{figure}}
|
||||||
|
{\end@float}
|
||||||
|
\newenvironment{figure*}
|
||||||
|
{\@dblfloat{figure}}
|
||||||
|
{\end@dblfloat}
|
||||||
|
\newcounter{table}
|
||||||
|
\renewcommand\thetable{\@arabic\c@table}
|
||||||
|
\def\fps@table{tbp}
|
||||||
|
\def\ftype@table{2}
|
||||||
|
\def\ext@table{lot}
|
||||||
|
\def\fnum@table{\tablename\nobreakspace\thetable}
|
||||||
|
\newenvironment{table}
|
||||||
|
{\@float{table}}
|
||||||
|
{\end@float}
|
||||||
|
\newenvironment{table*}
|
||||||
|
{\@dblfloat{table}}
|
||||||
|
{\end@dblfloat}
|
||||||
|
\newlength\abovecaptionskip
|
||||||
|
\newlength\belowcaptionskip
|
||||||
|
\setlength\abovecaptionskip{10\p@}
|
||||||
|
\setlength\belowcaptionskip{0\p@}
|
||||||
|
\long\def\@makecaption#1#2{%
|
||||||
|
\vskip\abovecaptionskip
|
||||||
|
\sbox\@tempboxa{#1: #2}%
|
||||||
|
\ifdim \wd\@tempboxa >\hsize
|
||||||
|
#1: #2\par
|
||||||
|
\else
|
||||||
|
\global \@minipagefalse
|
||||||
|
\hb@xt@\hsize{\hfil\box\@tempboxa\hfil}%
|
||||||
|
\fi
|
||||||
|
\vskip\belowcaptionskip}
|
||||||
|
\DeclareOldFontCommand{\rm}{\normalfont\rmfamily}{\mathrm}
|
||||||
|
\DeclareOldFontCommand{\sf}{\normalfont\sffamily}{\mathsf}
|
||||||
|
\DeclareOldFontCommand{\tt}{\normalfont\ttfamily}{\mathtt}
|
||||||
|
\DeclareOldFontCommand{\bf}{\normalfont\bfseries}{\mathbf}
|
||||||
|
\DeclareOldFontCommand{\it}{\normalfont\itshape}{\mathit}
|
||||||
|
\DeclareOldFontCommand{\sl}{\normalfont\slshape}{\@nomath\sl}
|
||||||
|
\DeclareOldFontCommand{\sc}{\normalfont\scshape}{\@nomath\sc}
|
||||||
|
\DeclareRobustCommand*\cal{\@fontswitch\relax\mathcal}
|
||||||
|
\DeclareRobustCommand*\mit{\@fontswitch\relax\mathnormal}
|
||||||
|
\newcommand\@pnumwidth{1.55em}
|
||||||
|
\newcommand\@tocrmarg{2.55em}
|
||||||
|
\newcommand\@dotsep{4.5}
|
||||||
|
\setcounter{tocdepth}{3}
|
||||||
|
\newcommand\tableofcontents{%
|
||||||
|
\section*{\contentsname
|
||||||
|
\@mkboth{%
|
||||||
|
\MakeUppercase\contentsname}{\MakeUppercase\contentsname}}%
|
||||||
|
\@starttoc{toc}%
|
||||||
|
}
|
||||||
|
\newcommand*\l@part[2]{%
|
||||||
|
\ifnum \c@tocdepth >-2\relax
|
||||||
|
\addpenalty\@secpenalty
|
||||||
|
\addvspace{2.25em \@plus\p@}%
|
||||||
|
\setlength\@tempdima{3em}%
|
||||||
|
\begingroup
|
||||||
|
\parindent \z@ \rightskip \@pnumwidth
|
||||||
|
\parfillskip -\@pnumwidth
|
||||||
|
{\leavevmode
|
||||||
|
\large \bfseries #1\hfil \hb@xt@\@pnumwidth{\hss #2}}\par
|
||||||
|
\nobreak
|
||||||
|
\if@compatibility
|
||||||
|
\global\@nobreaktrue
|
||||||
|
\everypar{\global\@nobreakfalse\everypar{}}%
|
||||||
|
\fi
|
||||||
|
\endgroup
|
||||||
|
\fi}
|
||||||
|
\newcommand*\l@section[2]{%
|
||||||
|
\ifnum \c@tocdepth >\z@
|
||||||
|
\addpenalty\@secpenalty
|
||||||
|
\addvspace{1.0em \@plus\p@}%
|
||||||
|
\setlength\@tempdima{1.5em}%
|
||||||
|
\begingroup
|
||||||
|
\parindent \z@ \rightskip \@pnumwidth
|
||||||
|
\parfillskip -\@pnumwidth
|
||||||
|
\leavevmode \bfseries
|
||||||
|
\advance\leftskip\@tempdima
|
||||||
|
\hskip -\leftskip
|
||||||
|
#1\nobreak\hfil \nobreak\hb@xt@\@pnumwidth{\hss #2}\par
|
||||||
|
\endgroup
|
||||||
|
\fi}
|
||||||
|
\newcommand*\l@subsection{\@dottedtocline{2}{1.5em}{2.3em}}
|
||||||
|
\newcommand*\l@subsubsection{\@dottedtocline{3}{3.8em}{3.2em}}
|
||||||
|
\newcommand*\l@paragraph{\@dottedtocline{4}{7.0em}{4.1em}}
|
||||||
|
\newcommand*\l@subparagraph{\@dottedtocline{5}{10em}{5em}}
|
||||||
|
\newcommand\listoffigures{%
|
||||||
|
\section*{\listfigurename}%
|
||||||
|
\@mkboth{\MakeUppercase\listfigurename}%
|
||||||
|
{\MakeUppercase\listfigurename}%
|
||||||
|
\@starttoc{lof}%
|
||||||
|
}
|
||||||
|
\newcommand*\l@figure{\@dottedtocline{1}{1.5em}{2.3em}}
|
||||||
|
\newcommand\listoftables{%
|
||||||
|
\section*{\listtablename}%
|
||||||
|
\@mkboth{%
|
||||||
|
\MakeUppercase\listtablename}%
|
||||||
|
{\MakeUppercase\listtablename}%
|
||||||
|
\@starttoc{lot}%
|
||||||
|
}
|
||||||
|
\let\l@table\l@figure
|
||||||
|
\newdimen\bibindent
|
||||||
|
\setlength\bibindent{1.5em}
|
||||||
|
\newenvironment{thebibliography}[1]
|
||||||
|
{\section*{\refname}%
|
||||||
|
\@mkboth{\MakeUppercase\refname}{\MakeUppercase\refname}%
|
||||||
|
\list{\@biblabel{\@arabic\c@enumiv}}%
|
||||||
|
{\settowidth\labelwidth{\@biblabel{#1}}%
|
||||||
|
\leftmargin\labelwidth
|
||||||
|
\advance\leftmargin\labelsep
|
||||||
|
\@openbib@code
|
||||||
|
\usecounter{enumiv}%
|
||||||
|
\let\p@enumiv\@empty
|
||||||
|
\renewcommand\theenumiv{\@arabic\c@enumiv}}%
|
||||||
|
\sloppy
|
||||||
|
\clubpenalty4000
|
||||||
|
\@clubpenalty \clubpenalty
|
||||||
|
\widowpenalty4000%
|
||||||
|
\sfcode`\.\@m}
|
||||||
|
{\def\@noitemerr
|
||||||
|
{\@latex@warning{Empty `thebibliography' environment}}%
|
||||||
|
\endlist}
|
||||||
|
\newcommand\newblock{\hskip .11em\@plus.33em\@minus.07em}
|
||||||
|
\let\@openbib@code\@empty
|
||||||
|
\newenvironment{theindex}
|
||||||
|
{\if@twocolumn
|
||||||
|
\@restonecolfalse
|
||||||
|
\else
|
||||||
|
\@restonecoltrue
|
||||||
|
\fi
|
||||||
|
\twocolumn[\section*{\indexname}]%
|
||||||
|
\@mkboth{\MakeUppercase\indexname}%
|
||||||
|
{\MakeUppercase\indexname}%
|
||||||
|
\thispagestyle{plain}\parindent\z@
|
||||||
|
\parskip\z@ \@plus .3\p@\relax
|
||||||
|
\columnseprule \z@
|
||||||
|
\columnsep 35\p@
|
||||||
|
\let\item\@idxitem}
|
||||||
|
{\if@restonecol\onecolumn\else\clearpage\fi}
|
||||||
|
\newcommand\@idxitem{\par\hangindent 40\p@}
|
||||||
|
\newcommand\subitem{\@idxitem \hspace*{20\p@}}
|
||||||
|
\newcommand\subsubitem{\@idxitem \hspace*{30\p@}}
|
||||||
|
\newcommand\indexspace{\par \vskip 10\p@ \@plus5\p@ \@minus3\p@\relax}
|
||||||
|
\renewcommand\footnoterule{%
|
||||||
|
\kern-3\p@
|
||||||
|
\hrule\@width.4\columnwidth
|
||||||
|
\kern2.6\p@}
|
||||||
|
\newcommand\@makefntext[1]{%
|
||||||
|
\parindent 1em%
|
||||||
|
\noindent
|
||||||
|
\hb@xt@1.8em{\hss\@makefnmark}#1}
|
||||||
|
\newcommand\contentsname{Contents}
|
||||||
|
\newcommand\listfigurename{List of Figures}
|
||||||
|
\newcommand\listtablename{List of Tables}
|
||||||
|
\newcommand\refname{References}
|
||||||
|
\newcommand\indexname{Index}
|
||||||
|
\newcommand\figurename{Figure}
|
||||||
|
\newcommand\tablename{Table}
|
||||||
|
\newcommand\partname{Part}
|
||||||
|
\newcommand\appendixname{Appendix}
|
||||||
|
\newcommand\abstractname{}
|
||||||
|
\def\today{\ifcase\month\or
|
||||||
|
January\or February\or March\or April\or May\or June\or
|
||||||
|
July\or August\or September\or October\or November\or December\fi
|
||||||
|
\space\number\day, \number\year}
|
||||||
|
\setlength\columnsep{10\p@}
|
||||||
|
\setlength\columnseprule{0\p@}
|
||||||
|
\pagestyle{plain}
|
||||||
|
\pagenumbering{arabic}
|
||||||
|
|
||||||
|
% === ARLIMS adaptions following ============================================
|
||||||
|
\pagestyle{empty}
|
||||||
|
|
||||||
|
\setlength{\oddsidemargin}{1.5 cm}
|
||||||
|
\setlength{\evensidemargin}{-0.5 cm}
|
||||||
|
\setlength{\textheight}{22 cm}
|
||||||
|
\setlength{\textwidth}{15 cm}
|
||||||
|
\setlength{\topmargin}{0 cm}
|
||||||
|
|
||||||
|
% --- special indexing to cope with the list of articles
|
||||||
|
\newif\ifIndexMade
|
||||||
|
\def\makeartIndex{%
|
||||||
|
\protected@write1{}{\string\global\string\IndexMadetrue}
|
||||||
|
\newwrite\@artIndexfile
|
||||||
|
\immediate\openout\@artIndexfile=\jobname.adx
|
||||||
|
\def\artIndex{\@bsphack\begingroup\@sanitize\@wrartIndex}
|
||||||
|
\typeout{Writing artIndex file \jobname.adx}%
|
||||||
|
\let\makeartIndex\@empty
|
||||||
|
}
|
||||||
|
|
||||||
|
\def\@wrartIndex#1{%
|
||||||
|
\protected@write\@artIndexfile{}{%
|
||||||
|
\string\artIndexentry{\factTitle}%
|
||||||
|
{\thepage}%
|
||||||
|
{\string\last#1}%
|
||||||
|
{\factAuthor\factAuthorB\factAuthorC\factAuthorD}}%
|
||||||
|
\endgroup%
|
||||||
|
\@esphack%
|
||||||
|
}
|
||||||
|
|
||||||
|
\def\artIndex{\@bsphack\begingroup \@sanitize\@artIndex}
|
||||||
|
\def\@artIndex#1{\endgroup\@esphack}
|
||||||
|
|
||||||
|
\def\artIndexLast#1{%
|
||||||
|
\protected@write1{}{\string\gdef\string\last#1{\thepage}}%
|
||||||
|
}
|
||||||
|
|
||||||
|
% --- to set up the headers permanently
|
||||||
|
\newcommand{\markperm}[2]{%
|
||||||
|
\global\def\@evenhead{\arabic{page}\hfil{#1}}%
|
||||||
|
\global\def\@oddhead{{#2}\hfil\arabic{page}}
|
||||||
|
}
|
||||||
|
% (yes, I know I've hardwired arabic page numbering)
|
||||||
|
|
||||||
|
% --- to set up the headers with a once off first
|
||||||
|
\newcommand{\markonce}[3]{%
|
||||||
|
\global\def\@evenhead{\arabic{page}\hfil{#1}\markperm{#2}{#3}}%
|
||||||
|
\global\def\@oddhead{{#1}\hfil\arabic{page}\markperm{#2}{#3}}%
|
||||||
|
}
|
||||||
|
|
||||||
|
%% Put a line after the abstract %%
|
||||||
|
\newcommand{\drawline}{%
|
||||||
|
\vspace*{1mm}
|
||||||
|
\begin{center}
|
||||||
|
\rule{\textwidth}{0.2mm}
|
||||||
|
\end{center}
|
||||||
|
\vspace*{1mm}
|
||||||
|
}
|
||||||
|
|
||||||
|
\newcommand{\reff}[1]{(\ref{#1})}
|
||||||
|
|
||||||
|
|
||||||
|
% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
% Define the author and title commands. Bascially each just stuffs
|
||||||
|
% the argument into a storage command. I need to define the blank
|
||||||
|
% storage commands because I am using the "renewcomand", since I
|
||||||
|
% need to do this for each article.
|
||||||
|
|
||||||
|
\def\author{}
|
||||||
|
\def\institute{}
|
||||||
|
|
||||||
|
\def\factTitle{}
|
||||||
|
|
||||||
|
\def\factAuthor{}
|
||||||
|
\def\factAuthorB{}
|
||||||
|
\def\factAuthorC{}
|
||||||
|
\def\factAuthorD{}
|
||||||
|
|
||||||
|
\def\factInstitute{}
|
||||||
|
\def\factInstituteB{}
|
||||||
|
\def\factInstituteC{}
|
||||||
|
\def\factInstituteD{}
|
||||||
|
|
||||||
|
\def\footInst{}
|
||||||
|
\def\footInstB{}
|
||||||
|
\def\footInstC{}
|
||||||
|
\def\footInstD{}
|
||||||
|
|
||||||
|
\def\footAuthorInst{}
|
||||||
|
\def\footAuthorInstB{}
|
||||||
|
\def\footAuthorInstC{}
|
||||||
|
\def\footAuthorInstD{}
|
||||||
|
|
||||||
|
\renewcommand{\author}[2][]{
|
||||||
|
\renewcommand{\footAuthorInst}{#1}
|
||||||
|
\renewcommand{\factAuthor}{#2}
|
||||||
|
}
|
||||||
|
|
||||||
|
\newcommand{\authorB}[2][]{
|
||||||
|
\renewcommand{\footAuthorInstB}{#1}
|
||||||
|
\renewcommand{\factAuthorB}{#2}
|
||||||
|
}
|
||||||
|
|
||||||
|
\newcommand{\authorC}[2][]{
|
||||||
|
\renewcommand{\footAuthorInstC}{#1}
|
||||||
|
\renewcommand{\factAuthorC}{#2}
|
||||||
|
}
|
||||||
|
|
||||||
|
\newcommand{\authorD}[2][]{
|
||||||
|
\renewcommand{\footAuthorInstD}{#1}
|
||||||
|
\renewcommand{\factAuthorD}{#2}
|
||||||
|
}
|
||||||
|
|
||||||
|
\renewcommand{\institute}[2][]{
|
||||||
|
\renewcommand{\footInst}{#1}
|
||||||
|
\renewcommand{\factInstitute}{#2}
|
||||||
|
}
|
||||||
|
|
||||||
|
\newcommand{\instituteB}[2][*]{
|
||||||
|
\renewcommand{\footInstB}{#1}
|
||||||
|
\renewcommand{\factInstituteB}{#2}
|
||||||
|
}
|
||||||
|
|
||||||
|
\newcommand{\instituteC}[2][**]{
|
||||||
|
\renewcommand{\footInstC}{#1}
|
||||||
|
\renewcommand{\factInstituteC}{#2}
|
||||||
|
}
|
||||||
|
|
||||||
|
\newcommand{\instituteD}[2][***]{
|
||||||
|
\renewcommand{\footInstD}{#1}
|
||||||
|
\renewcommand{\factInstituteD}{#2}
|
||||||
|
}
|
||||||
|
|
||||||
|
\def\nameTag{}
|
||||||
|
|
||||||
|
\renewcommand{\title}[1]{
|
||||||
|
\renewcommand{\factTitle}{#1}
|
||||||
|
}
|
||||||
|
|
||||||
|
\newcommand{\iimsaddress}{
|
||||||
|
\begin{center}
|
||||||
|
\textit{Institute of Information \& Mathematical Sciences\\
|
||||||
|
Massey University at Albany, Auckland, New Zealand.}
|
||||||
|
\end{center}
|
||||||
|
}
|
||||||
|
|
||||||
|
\newcommand{\maketitle}[1][\factTitle]{
|
||||||
|
\begin{center}
|
||||||
|
\markonce{
|
||||||
|
\parbox{10cm}{\small
|
||||||
|
\textit{The Python Papers Source Codes,} Vol.~1 (2009)\\[1mm]
|
||||||
|
Available online at http://ojs.pythonpapers.org/index.php/tppsc/issue/view/13
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{\factAuthor\factAuthorB\factAuthorC\factAuthorD}
|
||||||
|
{#1}
|
||||||
|
|
||||||
|
\vspace*{1cm}
|
||||||
|
|
||||||
|
\begin{minipage}{12cm}
|
||||||
|
\begin{center}
|
||||||
|
{\bfseries\Large\factTitle\par}
|
||||||
|
\vspace*{2mm}
|
||||||
|
\end{center}
|
||||||
|
\end{minipage}
|
||||||
|
|
||||||
|
\vspace*{3mm}
|
||||||
|
|
||||||
|
\textsc{%
|
||||||
|
\factAuthor $^{\footAuthorInst}$%
|
||||||
|
\factAuthorB $^{\footAuthorInstB}$%
|
||||||
|
\factAuthorC $^{\footAuthorInstC}$%
|
||||||
|
\factAuthorD $^{\footAuthorInstD}$%
|
||||||
|
}
|
||||||
|
|
||||||
|
\vspace*{1mm}
|
||||||
|
|
||||||
|
$^{\footInst}$\textit{\factInstitute}
|
||||||
|
$^{\footInstB}$\textit{\factInstituteB}
|
||||||
|
$^{\footInstC}$\textit{\factInstituteC}
|
||||||
|
$^{\footInstD}$\textit{\factInstituteD}
|
||||||
|
|
||||||
|
\artIndex{\nameTag}
|
||||||
|
\end{center}
|
||||||
|
}
|
||||||
|
|
||||||
|
% calculate the margins so that the text on opposite sides
|
||||||
|
% of the same sheet is lined up at the edges
|
||||||
|
% ok, its a fudge, but you can change the oddsidemargin
|
||||||
|
% to shift both sides in sync.
|
||||||
|
|
||||||
|
\oddsidemargin1cm
|
||||||
|
\evensidemargin\paperwidth
|
||||||
|
\addtolength{\evensidemargin}{-\oddsidemargin}
|
||||||
|
\addtolength{\evensidemargin}{-205mm}
|
||||||
|
|
||||||
|
% --- turn off a few things -------------------------------------------------
|
||||||
|
\renewcommand{\markboth}[2]{}
|
||||||
|
\renewcommand{\markright}[1]{}
|
||||||
|
\newcommand{\markheading}[1]{}
|
||||||
|
|
||||||
|
\usepackage{amsmath,amsfonts,dsfont}
|
||||||
|
\usepackage{amsthm}
|
||||||
|
|
||||||
|
\newif\ifCombinedRlims
|
||||||
|
|
||||||
|
% === end of ARLIMS adaptions ===============================================
|
||||||
|
|
||||||
|
\if@twoside
|
||||||
|
\else
|
||||||
|
\raggedbottom
|
||||||
|
\fi
|
||||||
|
\if@twocolumn
|
||||||
|
\twocolumn
|
||||||
|
\sloppy
|
||||||
|
\flushbottom
|
||||||
|
\else
|
||||||
|
\onecolumn
|
||||||
|
\fi
|
||||||
|
\endinput
|
||||||
|
%%
|
||||||
|
%% End of file `arlims.cls'.
|
||||||
76
doc/KPyCon2009/code/beerdistribution.py
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
"""
|
||||||
|
The Beer Distribution Problem for the PuLP Modeller
|
||||||
|
|
||||||
|
Authors: Antony Phillips, Dr Stuart Mitchell 2007
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Import PuLP modeler functions
|
||||||
|
import pulp
|
||||||
|
|
||||||
|
# Creates a list of all the supply nodes
|
||||||
|
warehouses = ["A", "B"]
|
||||||
|
|
||||||
|
# Creates a dictionary for the number of units of supply for each supply node
|
||||||
|
supply = {"A": 1000,
|
||||||
|
"B": 4000}
|
||||||
|
|
||||||
|
# Creates a list of all demand nodes
|
||||||
|
bars = ["1", "2", "3", "4", "5"]
|
||||||
|
|
||||||
|
# Creates a dictionary for the number of units of demand for each demand node
|
||||||
|
demand = {"1":500,
|
||||||
|
"2":900,
|
||||||
|
"3":1800,
|
||||||
|
"4":200,
|
||||||
|
"5":700,}
|
||||||
|
|
||||||
|
# Creates a list of costs of each transportation path
|
||||||
|
costs = [ #Bars
|
||||||
|
#1 2 3 4 5
|
||||||
|
[2,4,5,2,1],#A Warehouses
|
||||||
|
[3,1,3,2,3] #B
|
||||||
|
]
|
||||||
|
|
||||||
|
# The cost data is made into a dictionary
|
||||||
|
costs = pulp.makeDict([warehouses, bars], costs,0)
|
||||||
|
|
||||||
|
# Creates the 'prob' variable to contain the problem data
|
||||||
|
prob = pulp.LpProblem("Beer Distribution Problem", pulp.LpMinimize)
|
||||||
|
|
||||||
|
# Creates a list of tuples containing all the possible routes for transport
|
||||||
|
routes = [(w,b) for w in warehouses for b in bars]
|
||||||
|
|
||||||
|
# A dictionary called x is created to contain quantity shipped on the routes
|
||||||
|
x = pulp.LpVariable.dicts("route", (warehouses, bars),
|
||||||
|
lowBound = 0
|
||||||
|
cat = pulp.LpInteger)
|
||||||
|
|
||||||
|
# The objective function is added to 'prob' first
|
||||||
|
prob += sum([x[w][b]*costs[w][b] for (w,b) in routes]), \
|
||||||
|
"Sum_of_Transporting_Costs"
|
||||||
|
|
||||||
|
# Supply maximum constraints are added to prob for each supply node (warehouse)
|
||||||
|
for w in warehouses:
|
||||||
|
prob += sum([x[w][b] for b in bars]) <= supply[w], \
|
||||||
|
"Sum_of_Products_out_of_Warehouse_%s"%w
|
||||||
|
|
||||||
|
# Demand minimum constraints are added to prob for each demand node (bar)
|
||||||
|
for b in bars:
|
||||||
|
prob += sum([x[w][b] for w in warehouses]) >= demand[b], \
|
||||||
|
"Sum_of_Products_into_Bar%s"%b
|
||||||
|
|
||||||
|
# The problem data is written to an .lp file
|
||||||
|
prob.writeLP("BeerDistributionProblem.lp")
|
||||||
|
|
||||||
|
# The problem is solved using PuLP's choice of Solver
|
||||||
|
prob.solve()
|
||||||
|
|
||||||
|
# The status of the solution is printed to the screen
|
||||||
|
print "Status:", pulp.LpStatus[prob.status]
|
||||||
|
|
||||||
|
# Each of the variables is printed with it's resolved optimum value
|
||||||
|
for v in prob.variables():
|
||||||
|
print v.name, "=", v.varValue
|
||||||
|
|
||||||
|
# The optimised objective function value is printed to the screen
|
||||||
|
print "Total Cost of Transportation = ", prob.objective.value()
|
||||||
50
doc/KPyCon2009/code/wedding.py
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
"""
|
||||||
|
A set partitioning model of a wedding seating problem
|
||||||
|
|
||||||
|
Authors: Stuart Mitchell 2009
|
||||||
|
"""
|
||||||
|
|
||||||
|
import pulp
|
||||||
|
|
||||||
|
max_tables = 5
|
||||||
|
max_table_size = 4
|
||||||
|
guests = 'A B C D E F G I J K L M N O P Q R'.split()
|
||||||
|
|
||||||
|
def happiness(table):
|
||||||
|
"""
|
||||||
|
Find the happiness of the table
|
||||||
|
- by calculating the maximum distance between the letters
|
||||||
|
"""
|
||||||
|
return abs(ord(table[0]) - ord(table[-1]))
|
||||||
|
|
||||||
|
#create list of all possible tables
|
||||||
|
possible_tables = [tuple(c) for c in pulp.allcombinations(guests,
|
||||||
|
max_table_size)]
|
||||||
|
|
||||||
|
#create a binary variable to state that a table setting is used
|
||||||
|
x = pulp.LpVariable.dicts('table', possible_tables,
|
||||||
|
lowBound = 0,
|
||||||
|
upBound = 1,
|
||||||
|
cat = pulp.LpInteger)
|
||||||
|
|
||||||
|
seating_model = pulp.LpProblem("Wedding Seating Model", pulp.LpMinimize)
|
||||||
|
|
||||||
|
seating_model += sum([happiness(table) * x[table] for table in possible_tables])
|
||||||
|
|
||||||
|
#specify the maximum number of tables
|
||||||
|
seating_model += sum([x[table] for table in possible_tables]) <= max_tables, \
|
||||||
|
"Maximum_number_of_tables"
|
||||||
|
|
||||||
|
#A guest must seated at one and only one table
|
||||||
|
for guest in guests:
|
||||||
|
seating_model += sum([x[table] for table in possible_tables
|
||||||
|
if guest in table]) == 1, "Must_seat_%s"%guest
|
||||||
|
|
||||||
|
seating_model.solve()
|
||||||
|
|
||||||
|
print "The choosen tables are out of a total of %s:"%len(possible_tables)
|
||||||
|
for table in possible_tables:
|
||||||
|
if x[table].value() == 1.0:
|
||||||
|
print table
|
||||||
|
|
||||||
|
|
||||||
38
doc/KPyCon2009/code/whiskas.py
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
"""
|
||||||
|
Example problem file that solves the whiskas blending problem
|
||||||
|
"""
|
||||||
|
import pulp
|
||||||
|
|
||||||
|
#initialise the model
|
||||||
|
whiskas_model = pulp.LpProblem('The Whiskas Problem', pulp.LpMinimize)
|
||||||
|
# make a list of ingredients
|
||||||
|
ingredients = ['chicken', 'beef', 'mutton', 'rice', 'wheat', 'gel']
|
||||||
|
# create a dictionary of pulp variables with keys from ingredients
|
||||||
|
# the default lower bound is -inf
|
||||||
|
x = pulp.LpVariable.dict('x_%s', ingredients, lowBound =0)
|
||||||
|
|
||||||
|
# cost data
|
||||||
|
cost = dict(zip(ingredients, [0.013, 0.008, 0.010, 0.002, 0.005, 0.001]))
|
||||||
|
# create the objective
|
||||||
|
whiskas_model += sum( [cost[i] * x[i] for i in ingredients])
|
||||||
|
|
||||||
|
# ingredient parameters
|
||||||
|
protein = dict(zip(ingredients, [0.100, 0.200, 0.150, 0.000, 0.040, 0.000]))
|
||||||
|
fat = dict(zip(ingredients, [0.080, 0.100, 0.110, 0.010, 0.010, 0.000]))
|
||||||
|
fibre = dict(zip(ingredients, [0.001, 0.005, 0.003, 0.100, 0.150, 0.000]))
|
||||||
|
salt = dict(zip(ingredients, [0.002, 0.005, 0.007, 0.002, 0.008, 0.000]))
|
||||||
|
|
||||||
|
#note these are constraints and not an objective as there is a equality/inequality
|
||||||
|
whiskas_model += sum([protein[i]*x[i] for i in ingredients]) >= 8.0
|
||||||
|
whiskas_model += sum([fat[i]*x[i] for i in ingredients]) >= 6.0
|
||||||
|
whiskas_model += sum([fibre[i]*x[i] for i in ingredients]) <= 2.0
|
||||||
|
whiskas_model += sum([salt[i]*x[i] for i in ingredients]) <= 0.4
|
||||||
|
|
||||||
|
#problem is then solved with the default solver
|
||||||
|
whiskas_model.solve()
|
||||||
|
|
||||||
|
#print the result
|
||||||
|
for ingredient in ingredients:
|
||||||
|
print 'The mass of %s is %s grams per can'%(ingredient,
|
||||||
|
x[ingredient].value())
|
||||||
|
|
||||||
BIN
doc/KPyCon2009/images/beerdistribution.png
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
doc/KPyCon2009/images/whiskas_blend.jpg
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
doc/KPyCon2009/images/whiskas_ingredients.jpg
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
doc/KPyCon2009/images/whiskas_label.jpg
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
doc/KPyCon2009/images/whiskas_nutrition.jpg
Normal file
|
After Width: | Height: | Size: 21 KiB |
97
doc/KPyCon2009/references.bib
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
% This file was created with JabRef 2.5.
|
||||||
|
% Encoding: ISO8859_1
|
||||||
|
|
||||||
|
@InProceedings{ flopc,
|
||||||
|
title = "FLOPC++ An Algebraic Modeling Language Embedded in C++",
|
||||||
|
booktitle = "Operations Research Proceedings 2006",
|
||||||
|
series = "Operations Research Proceedings",
|
||||||
|
author = "Tim Helge Hultberg",
|
||||||
|
editor = "Karl-Heinz Waldmann and Ulrike M. Stocker",
|
||||||
|
publisher = "Springer Berlin Heidelberg",
|
||||||
|
location = "Karlsruhe",
|
||||||
|
pages = "187--190",
|
||||||
|
number = "6",
|
||||||
|
year = "2006",
|
||||||
|
abstract = "FLOPC++ is an open source algebraic modeling language implemented as a C++ class library. It allows linear optimization problems to be modeled in a declarative style, similar to algebraic modeling languages, such as GAMS and AMPL, within a C++ program. The project is part of COmputational INfrastructure for Operations Research (COIN-OR) and uses its Open Solver Interface (OSI) to achieve solver independence."
|
||||||
|
}
|
||||||
|
|
||||||
|
@Misc{ gams,
|
||||||
|
author = "A. Brooke and D. Kendrick and A. Meeraus",
|
||||||
|
title = "GAMS: A User's Guide",
|
||||||
|
text = "A. Brooke, D. Kendrick, and A. Meeraus. GAMS: A User's Guide, Release 2.25. The Scientific Press, 1992.",
|
||||||
|
year = "1992",
|
||||||
|
url = "citeseer.ist.psu.edu/brooke92gams.html"
|
||||||
|
}
|
||||||
|
|
||||||
|
@Article{ ampl,
|
||||||
|
title = "AMPL: A Mathematical Programming Language",
|
||||||
|
author = "Robert Fourer and David M. Gay and Brian W. Kernighan",
|
||||||
|
journal = "Management Science",
|
||||||
|
pages = "519--554",
|
||||||
|
volume = "36",
|
||||||
|
year = "1990"
|
||||||
|
}
|
||||||
|
|
||||||
|
@Article{ columngeneration,
|
||||||
|
title = "Selected Topics in Column Generation ",
|
||||||
|
author = "Marco E. L{\"u}bbecke and Jacques Desrosiers",
|
||||||
|
journal = "OPERATIONS RESEARCH",
|
||||||
|
pages = "1007--1023",
|
||||||
|
volume = "53",
|
||||||
|
number = "6",
|
||||||
|
month = "November- December",
|
||||||
|
year = "2005",
|
||||||
|
doi = "10.1287/opre.1050.0234"
|
||||||
|
}
|
||||||
|
|
||||||
|
@Electronic{ pulpwiki,
|
||||||
|
title = "Pulp-or wiki",
|
||||||
|
author = "Stuart A Mitchell and Antony Phillips",
|
||||||
|
url = "http://pulp-or.googlecode.com"
|
||||||
|
}
|
||||||
|
|
||||||
|
@Electronic{ scienceofbetter,
|
||||||
|
title = "Operations Research: The Science of Better",
|
||||||
|
url = "http://www.scienceofbetter.org/"
|
||||||
|
}
|
||||||
|
|
||||||
|
@Article{ coin-or,
|
||||||
|
title = "The Common Optimization INterface for Operations Research",
|
||||||
|
author = "Robin Lougee-Heimer",
|
||||||
|
journal = "IBM Journal of Research and Development",
|
||||||
|
pages = "57--66",
|
||||||
|
volume = "47",
|
||||||
|
number = "1",
|
||||||
|
month = "January",
|
||||||
|
year = "2003"
|
||||||
|
}
|
||||||
|
|
||||||
|
@Electronic{ glpk,
|
||||||
|
title = "GNU Linear Programming Kit",
|
||||||
|
url = "http://www.gnu.org/software/glpk/glpk.html"
|
||||||
|
}
|
||||||
|
|
||||||
|
@Electronic{ cplex,
|
||||||
|
title = "Cplex Website",
|
||||||
|
url = "http://www.ilog.com/products/cplex/"
|
||||||
|
}
|
||||||
|
|
||||||
|
@Electronic{ gurobi,
|
||||||
|
title = "Gurobi Website",
|
||||||
|
url = "http://www.gurobi.com/"
|
||||||
|
}
|
||||||
|
|
||||||
|
@Electronic{ coopr,
|
||||||
|
title = "Coopr: A COmmon Optimization Python Repository",
|
||||||
|
author = "William E. Hart",
|
||||||
|
url = "https://software.sandia.gov/trac/coopr"
|
||||||
|
}
|
||||||
|
|
||||||
|
@InProceedings{ pyomo,
|
||||||
|
title = "Python Optimization Modeling Objects (Pyomo)",
|
||||||
|
booktitle = "Proc INFORMS Computing Society Conference",
|
||||||
|
author = "William Hart",
|
||||||
|
year = "2009",
|
||||||
|
url = "http://www.optimization-online.org/DB_HTML/2008/09/2095.html"
|
||||||
|
}
|
||||||
|
|
||||||
BIN
doc/KiwiPycon.odp
Normal file
BIN
doc/KiwiPycon.pdf
Normal file
BIN
doc/OpenSourceOptimisation.odp
Normal file
BIN
doc/OpenSourceOptimisation.pdf
Normal file
BIN
doc/PulpOptimisation.odp
Normal file
BIN
doc/PulpOptimisation.pdf
Normal file
BIN
doc/_OptimisationWithPuLP.pdf
Normal file
BIN
doc/pulp.pdf
Normal file
5
doc/source/AUTHORS.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
+ Stuart Mitchell (s.mitchell@auckland.ac.nz)
|
||||||
|
+ Anita Kean
|
||||||
|
+ Andrew Mason
|
||||||
|
+ Michael O\'Sullivan
|
||||||
|
+ Antony Phillips
|
||||||
336
doc/source/CaseStudies/a_blending_problem.rst
Normal file
@@ -0,0 +1,336 @@
|
|||||||
|
A Blending Problem
|
||||||
|
===================
|
||||||
|
|
||||||
|
Problem Description
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
.. image:: images/whiskas_label.jpg
|
||||||
|
|
||||||
|
Whiskas cat food, shown above, is manufactured by Uncle Ben’s.
|
||||||
|
Uncle Ben’s want to produce their cat food products as cheaply as possible
|
||||||
|
while ensuring they meet the stated nutritional analysis requirements
|
||||||
|
shown on the cans. Thus they want to vary the quantities of each
|
||||||
|
ingredient used (the main ingredients being chicken, beef, mutton,
|
||||||
|
rice, wheat and gel) while still meeting their nutritional standards.
|
||||||
|
|
||||||
|
.. image:: images/whiskas_blend.jpg
|
||||||
|
|
||||||
|
The costs of the chicken, beef, and mutton are $0.013, $0.008 and
|
||||||
|
$0.010 respectively, while the costs of the rice, wheat and gel are
|
||||||
|
$0.002, $0.005 and $0.001 respectively. (All costs are per gram.) For
|
||||||
|
this exercise we will ignore the vitamin and mineral ingredients. (Any
|
||||||
|
costs for these are likely to be very small anyway.)
|
||||||
|
|
||||||
|
Each ingredient contributes to the total weight of protein, fat,
|
||||||
|
fibre and salt in the final product. The contributions (in grams) per
|
||||||
|
gram of ingredient are given in the table below.
|
||||||
|
|
||||||
|
|
||||||
|
============ ========= ========= ======== =======
|
||||||
|
Stuff Protein Fat Fibre Salt
|
||||||
|
============ ========= ========= ======== =======
|
||||||
|
Chicken 0.100 0.080 0.001 0.002
|
||||||
|
Beef 0.200 0.100 0.005 0.005
|
||||||
|
Rice 0.000 0.010 0.100 0.002
|
||||||
|
Wheat bran 0.040 0.010 0.150 0.008
|
||||||
|
============ ========= ========= ======== =======
|
||||||
|
|
||||||
|
Simplified Formulation
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
First we will consider a simplified problem to build a simple Python model.
|
||||||
|
|
||||||
|
Identify the Decision Variables
|
||||||
|
+++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
Assume Whiskas want to make their cat food out of just two ingredients:
|
||||||
|
Chicken and Beef. We will first define our decision variables:
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
x_1 &= \text{ percentage of chicken meat in a can of cat food }\\
|
||||||
|
x_2 &= \text{ percentage of beef used in a can of cat food }
|
||||||
|
|
||||||
|
The limitations on these variables (greater than zero) must be noted but
|
||||||
|
for the Python implementation, they are not entered or listed separately or with the other constraints.
|
||||||
|
|
||||||
|
Formulate the Objective Function
|
||||||
|
++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
The objective function becomes:
|
||||||
|
|
||||||
|
.. math:: \textbf{ min } 0.013 x_1 + 0.008 x_2
|
||||||
|
|
||||||
|
The Constraints
|
||||||
|
+++++++++++++++
|
||||||
|
|
||||||
|
The constraints on the variables are that they must sum to 100 and that the nutritional requirements are met:
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
1.000 x_1 + 1.000 x_2 &= 100.0\\
|
||||||
|
0.100 x_1 + 0.200 x_2 &\ge 8.0\\
|
||||||
|
0.080 x_1 + 0.100 x_2 &\ge 6.0\\
|
||||||
|
0.001 x_1 + 0.005 x_2 &\le 2.0\\
|
||||||
|
0.002 x_1 + 0.005 x_2 &\le 0.4\\
|
||||||
|
|
||||||
|
Solution to Simplified Problem
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
To obtain the solution to this Linear Program, we can write a short
|
||||||
|
program in Python to call PuLP's modelling functions, which will then
|
||||||
|
call a solver. This will explain step-by-step how to write this Python
|
||||||
|
program. It is suggested that you repeat the exercise yourself. The code
|
||||||
|
for this example is found in `WhiskasModel1.py <https://projects.coin-or.org/PuLP/browser/trunk/examples/WhiskasModel1.py?format=txt>`_
|
||||||
|
|
||||||
|
The start of the your file should then be headed with a short commenting section outlining the purpose of the program. For example:
|
||||||
|
|
||||||
|
.. literalinclude:: ../../../examples/WhiskasModel1.py
|
||||||
|
:lines: 1-5
|
||||||
|
|
||||||
|
Then you will import PuLP's functions for use in your code:
|
||||||
|
|
||||||
|
.. literalinclude:: ../../../examples/WhiskasModel1.py
|
||||||
|
:lines: 7-8
|
||||||
|
|
||||||
|
A variable called ``prob`` (although its name is not important) is
|
||||||
|
created using the :class:`~pulp.LpProblem` function. It has two parameters, the first
|
||||||
|
being the arbitrary name of this problem (as a string), and the second
|
||||||
|
parameter being either ``LpMinimize`` or ``LpMaximize`` depending on the
|
||||||
|
type of LP you are trying to solve:
|
||||||
|
|
||||||
|
.. literalinclude:: ../../../examples/WhiskasModel1.py
|
||||||
|
:lines: 10-11
|
||||||
|
|
||||||
|
The problem variables ``x1`` and ``x2`` are created using the
|
||||||
|
:class:`~pulp.LpVariable` class. It has four parameters, the first is the
|
||||||
|
arbitrary name of what this variable represents, the second is the lower
|
||||||
|
bound on this variable, the third is the upper bound, and the fourth
|
||||||
|
is essentially the type of data (discrete or continuous). The options
|
||||||
|
for the fourth parameter are ``LpContinuous`` or ``LpInteger``, with the
|
||||||
|
default as ``LpContinuous``. If we were modelling the number of cans
|
||||||
|
to produce, we would need to input ``LpInteger`` since it is discrete
|
||||||
|
data. The bounds can be entered directly as a number, or ``None`` to
|
||||||
|
represent no bound (i.e. positive or negative infinity), with ``None``
|
||||||
|
as the default. If the first few parameters are entered and the rest
|
||||||
|
are ignored (as shown), they take their default values. However, if you
|
||||||
|
wish to specify the third parameter, but you want the second to be the
|
||||||
|
default value, you will need to specifically set the second parameter as
|
||||||
|
it's default value. i.e you cannot leave a parameter entry blank.
|
||||||
|
e.g::
|
||||||
|
|
||||||
|
LpVariable("example", None, 100)
|
||||||
|
|
||||||
|
or::
|
||||||
|
|
||||||
|
LpVariable("example", upBound = 100)
|
||||||
|
|
||||||
|
To explicitly create the two variables needed for this problem:
|
||||||
|
|
||||||
|
.. literalinclude:: ../../../examples/WhiskasModel1.py
|
||||||
|
:lines: 13-15
|
||||||
|
|
||||||
|
The variable ``prob`` now begins collecting problem data with the
|
||||||
|
``+=`` operator. The objective function is logically entered first, with
|
||||||
|
an important comma ``,`` at the end of the statement and a short string
|
||||||
|
explaining what this objective function is:
|
||||||
|
|
||||||
|
.. literalinclude:: ../../../examples/WhiskasModel1.py
|
||||||
|
:lines: 17-18
|
||||||
|
|
||||||
|
The constraints are now entered (Note: any "non-negative"
|
||||||
|
constraints were already included when defining the variables). This is
|
||||||
|
done with the '+=' operator again, since we are adding more data to the
|
||||||
|
``prob`` variable. The constraint is logically entered after this, with a
|
||||||
|
comma at the end of the constraint equation and a brief description of
|
||||||
|
the cause of that constraint:
|
||||||
|
|
||||||
|
.. literalinclude:: ../../../examples/WhiskasModel1.py
|
||||||
|
:lines: 20-25
|
||||||
|
|
||||||
|
Now that all the problem data is entered, the :meth:`~pulp.LpProblem.writeLP` function
|
||||||
|
can be used to copy this information into a .lp file into the directory
|
||||||
|
that your code-block is running from. Once your code runs successfully, you
|
||||||
|
can open this .lp file with a text editor to see what the above steps were
|
||||||
|
doing. You will notice that there is no assignment operator (such as an
|
||||||
|
equals sign) on this line. This is because the function/method called
|
||||||
|
:meth:`~pulp.LpProblem.writeLP` is being performed to the
|
||||||
|
variable/object ``prob`` (and the
|
||||||
|
string ``"WhiskasModel.lp"`` is an additional parameter). The dot ``.``
|
||||||
|
between the variable/object and the function/method is important and is
|
||||||
|
seen frequently in Object Oriented software (such as this):
|
||||||
|
|
||||||
|
|
||||||
|
.. literalinclude:: ../../../examples/WhiskasModel1.py
|
||||||
|
:lines: 27-28
|
||||||
|
|
||||||
|
The LP is solved using the solver that PuLP chooses. The input
|
||||||
|
brackets after :meth:`~pulp.LpProblem.solve` are left empty in this case, however they can be
|
||||||
|
used to specify which solver to use (e.g ``prob.solve(CPLEX())`` ):
|
||||||
|
|
||||||
|
.. literalinclude:: ../../../examples/WhiskasModel1.py
|
||||||
|
:lines: 30-31
|
||||||
|
|
||||||
|
Now the results of the solver call can be displayed as output to
|
||||||
|
us. Firstly, we request the status of the solution, which can be one of
|
||||||
|
"Not Solved", "Infeasible", "Unbounded", "Undefined" or "Optimal". The
|
||||||
|
value of ``prob`` (:attr:`pulp.pulp.LpProblem.status`) is returned as an integer, which must be converted
|
||||||
|
to its significant text meaning using the
|
||||||
|
:attr:`~pulp.constants.LpStatus` dictionary. Since
|
||||||
|
:attr:`~pulp.constants.LpStatus` is a dictionary(:obj:`dict`), its input must be in square brackets:
|
||||||
|
|
||||||
|
.. literalinclude:: ../../../examples/WhiskasModel1.py
|
||||||
|
:lines: 33-34
|
||||||
|
|
||||||
|
The variables and their resolved optimum values can now be printed
|
||||||
|
to the screen.
|
||||||
|
|
||||||
|
.. literalinclude:: ../../../examples/WhiskasModel1.py
|
||||||
|
:lines: 36-38
|
||||||
|
|
||||||
|
The ``for`` loop makes ``variable`` cycle through all
|
||||||
|
the problem variable names (in this case just ``ChickenPercent`` and
|
||||||
|
``BeefPercent``). Then it prints each variable name, followed by an
|
||||||
|
equals sign, followed by its optimum value.
|
||||||
|
:attr:`~pulp.LpVariable.name` and
|
||||||
|
:attr:`~pulp.LpVariable.varValue` are
|
||||||
|
properties of the object ``variable``.
|
||||||
|
|
||||||
|
|
||||||
|
The optimised objective function value is printed to the screen,
|
||||||
|
using the value function. This ensures that the number is in the right
|
||||||
|
format to be displayed. :attr:`~pulp.LpProblem.objective` is an attribute of the object
|
||||||
|
``prob``:
|
||||||
|
|
||||||
|
.. literalinclude:: ../../../examples/WhiskasModel1.py
|
||||||
|
:lines: 40-41
|
||||||
|
|
||||||
|
Running this file should then produce the output to show that
|
||||||
|
Chicken will make up 33.33%, Beef will make up 66.67% and the
|
||||||
|
Total cost of ingredients per can is 96 cents.
|
||||||
|
|
||||||
|
Full Formulation
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Now we will formulate the problem fully with
|
||||||
|
all the variables. Whilst it could be implemented into Python with
|
||||||
|
little addition to our method above, we will look at a better way which
|
||||||
|
does not mix the problem data, and the formulation as much. This will
|
||||||
|
make it easier to change any problem data for other tests. We will start
|
||||||
|
the same way by algebraically defining the problem:
|
||||||
|
|
||||||
|
#. Identify the Decision Variables
|
||||||
|
For the Whiskas Cat Food Problem the decision variables are the percentages of
|
||||||
|
the different ingredients we include in the can.
|
||||||
|
Since the can is 100g, these percentages also represent the amount in g of each
|
||||||
|
ingredient included.
|
||||||
|
We must formally define our decision variables, being sure to state the units
|
||||||
|
we are using.
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
x_1 &= \text{percentage of chicken meat in a can of cat food}\\
|
||||||
|
x_2 &= \text{percentage of beef used in a can of cat food}\\
|
||||||
|
x_3 &= \text{percentage of mutton used in a can of cat food}\\
|
||||||
|
x_4 &= \text{percentage of rice used in a can of cat food}\\
|
||||||
|
x_5 &= \text{percentage of wheat bran used in a can of cat food}\\
|
||||||
|
x_6 &= \text{percentage of gel used in a can of cat food}
|
||||||
|
|
||||||
|
Note that these percentages must be between 0 and 100.
|
||||||
|
#. Formulate the Objective Function
|
||||||
|
For the Whiskas Cat Food Problem the objective is to minimise the total cost
|
||||||
|
of ingredients per can of cat food.
|
||||||
|
We know the cost per g of each ingredient. We decide the percentage of each
|
||||||
|
ingredient in the can, so we must divide by 100 and multiply by the weight of
|
||||||
|
the can in g. This will give us the weight in g of each
|
||||||
|
ingredient:
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
\min \$0.013 x_1 + \$0.008 x_2 + \$0.010 x_3 + \$0.002 x_4 + \$0.005 x_5 + \$0.001 x_6
|
||||||
|
|
||||||
|
#. Formulate the Constraints
|
||||||
|
The constraints for the Whiskas Cat Food Problem are that:
|
||||||
|
|
||||||
|
* The sum of the percentages must make up the whole can (= 100%).
|
||||||
|
* The stated nutritional analysis requirements are met.
|
||||||
|
|
||||||
|
The constraint for the "whole can" is:
|
||||||
|
|
||||||
|
.. math:: x_1 + x_2 + x_3 + x_4 + x_5 +x _6 = 100
|
||||||
|
|
||||||
|
To meet the nutritional analysis requirements, we need to have at
|
||||||
|
least 8g of Protein per 100g, 6g of fat, but no more than 2g of fibre
|
||||||
|
and 0.4g of salt. To formulate these constraints we make use of the
|
||||||
|
previous table of contributions from each ingredient. This allows us
|
||||||
|
to formulate the following constraints on the total contributions of
|
||||||
|
protein, fat, fibre and salt from the ingredients:
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
0.100 x_1 +0.200 x_2 +0.150 x_3 +0.000 x_4 +0.040 x_5 +0.0 x_6 0&\ge 8.0 \\
|
||||||
|
0.080 x_1 +0.100 x_2 +0.110 x_3 +0.010 x_4 +0.010 x_5 0+0.0 x_6 &\ge 6.0 \\
|
||||||
|
0.001 x_1 +0.005 x_2 +0.003 x_3 +0.100 x_4 0+0.150 x_5 +0.0 x_6 &\le 2.0 \\
|
||||||
|
0.002 x_1 +0.005 x_2 +0.007 x_3 0+0.002 x_4 +0.008 x_5 +0.0 x_6 &\le 0.4
|
||||||
|
|
||||||
|
Solution to Full Problem
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
To obtain the solution to this Linear Program, we again write a
|
||||||
|
short program in Python to call PuLP's modelling functions, which will
|
||||||
|
then call a solver. This will explain step-by-step how to write this
|
||||||
|
Python program with it's improvement to the above model. It is suggested
|
||||||
|
that you repeat the exercise yourself. The code for this example is
|
||||||
|
found in the `WhiskasModel2.py <https://projects.coin-or.org/PuLP/browser/trunk/examples/WhiskasModel2.py?format=txt>`_
|
||||||
|
|
||||||
|
As with last time, it is advisable to head your file with commenting on its
|
||||||
|
purpose, and the author name and date. Importing of the PuLP functions is also done in the same way:
|
||||||
|
|
||||||
|
.. literalinclude:: ../../../examples/WhiskasModel2.py
|
||||||
|
:lines: 1-8
|
||||||
|
|
||||||
|
Next, before the ``prob`` variable or type of problem are defined,
|
||||||
|
the key problem data is entered into dictionaries. This includes the
|
||||||
|
list of Ingredients, followed by the cost of each Ingredient, and it's
|
||||||
|
percentage of each of the four nutrients. These values are clearly laid
|
||||||
|
out and could easily be changed by someone with little knowledge of
|
||||||
|
programming. The ingredients are the reference keys, with the numbers as
|
||||||
|
the data.
|
||||||
|
|
||||||
|
.. literalinclude:: ../../../examples/WhiskasModel2.py
|
||||||
|
:lines: 10-51
|
||||||
|
|
||||||
|
The ``prob`` variable is created to contain the formulation, and the
|
||||||
|
usual parameters are passed into :obj:`~pulp.LpProblem`.
|
||||||
|
|
||||||
|
.. literalinclude:: ../../../examples/WhiskasModel2.py
|
||||||
|
:lines: 53-54
|
||||||
|
|
||||||
|
A dictionary called ``ingredient_vars`` is created which contains
|
||||||
|
the LP variables, with their defined lower bound of zero. The reference
|
||||||
|
keys to the dictionary are the Ingredient names, and the data is
|
||||||
|
``Ingr_IngredientName``. (e.g. MUTTON: Ingr_MUTTON)
|
||||||
|
|
||||||
|
.. literalinclude:: ../../../examples/WhiskasModel2.py
|
||||||
|
:lines: 56-57
|
||||||
|
|
||||||
|
Since ``costs`` and ``ingredient_vars`` are now dictionaries with the
|
||||||
|
reference keys as the Ingredient names, the data can be simply extracted
|
||||||
|
with a list comprehension as shown. The :func:`~pulp.lpSum` function will add the
|
||||||
|
elements of the resulting list. Thus the objective function is simply
|
||||||
|
entered and assigned a name:
|
||||||
|
|
||||||
|
.. literalinclude:: ../../../examples/WhiskasModel2.py
|
||||||
|
:lines: 59-60
|
||||||
|
|
||||||
|
Further list comprehensions are used to define the other 5 constraints, which are also each given names describing them.
|
||||||
|
|
||||||
|
.. literalinclude:: ../../../examples/WhiskasModel2.py
|
||||||
|
:lines: 62-67
|
||||||
|
|
||||||
|
Following this, the :ref:`writeLP<writeLP>` line etc follow exactly the same as
|
||||||
|
in the simplified example.
|
||||||
|
|
||||||
|
The optimal solution is 60% Beef and 40% Gel leading to a objective
|
||||||
|
Function value of 52 cents per can.
|
||||||
|
|
||||||
62
doc/source/CaseStudies/a_set_partitioning_problem.rst
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
A Set Partitioning Problem
|
||||||
|
============================
|
||||||
|
|
||||||
|
A set partitioning problem determines how the items in one set (S) can be partitioned into smaller
|
||||||
|
subsets. All items in S must be contained in one and only one partition. Related problems are:
|
||||||
|
|
||||||
|
+ set packing - all items must be contained in zero or one partitions;
|
||||||
|
+ set covering - all items must be contained in at least one partition.
|
||||||
|
|
||||||
|
In this case study a wedding planner must determine guest seating allocations
|
||||||
|
for a wedding. To model this problem the tables are modelled as the partitions
|
||||||
|
and the guests invited to the wedding are modelled as the elements of S. The
|
||||||
|
wedding planner wishes to maximise the total happiness of all of the tables.
|
||||||
|
|
||||||
|
.. image:: images/wedding_seating.jpg
|
||||||
|
|
||||||
|
.. raw:: html
|
||||||
|
|
||||||
|
<div xmlns:cc="http://creativecommons.org/ns#" about="http://www.flickr.com/photos/71463577@N00/3735357685"><a rel="cc:attributionURL" href="http://www.flickr.com/photos/mbphotography/">http://www.flickr.com/photos/mbphotography/</a> / <a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/2.0/">CC BY-NC-ND 2.0</a></div>
|
||||||
|
|
||||||
|
A set partitioning problem may be modelled by explicitly enumerating each
|
||||||
|
possible subset. Though this approach does become intractable for large numbers
|
||||||
|
of items (without using column generation) it does have the advantage that the
|
||||||
|
objective function co-efficients for the partitions can be non-linear
|
||||||
|
expressions (like happiness) and still allow this problem to be solved
|
||||||
|
using Linear Programming.
|
||||||
|
|
||||||
|
First we use :func:`~pulp.allcombinations` to generate a list of all
|
||||||
|
possible table seatings.
|
||||||
|
|
||||||
|
.. literalinclude:: ../../../examples/wedding.py
|
||||||
|
:lines: 20-22
|
||||||
|
|
||||||
|
Then we create a binary variable that will be 1 if the table will be in the solution, or zero otherwise.
|
||||||
|
|
||||||
|
.. literalinclude:: ../../../examples/wedding.py
|
||||||
|
:lines: 24-28
|
||||||
|
|
||||||
|
We create the :class:`~pulp.LpProblem` and then make the objective function. Note that
|
||||||
|
happiness function used in this script would be difficult to model in any other way.
|
||||||
|
|
||||||
|
.. literalinclude:: ../../../examples/wedding.py
|
||||||
|
:lines: 30-32
|
||||||
|
|
||||||
|
We specify the total number of tables allowed in the solution.
|
||||||
|
|
||||||
|
.. literalinclude:: ../../../examples/wedding.py
|
||||||
|
:lines: 34-35
|
||||||
|
|
||||||
|
This set of constraints defines the set partitioning problem by guaranteeing that a guest is allocated to
|
||||||
|
exactly one table.
|
||||||
|
|
||||||
|
.. literalinclude:: ../../../examples/wedding.py
|
||||||
|
:lines: 38-41
|
||||||
|
|
||||||
|
The full file can be found here `wedding.py <https://projects.coin-or.org/PuLP/browser/trunk/examples/wedding.py?format=txt>`_
|
||||||
|
|
||||||
|
.. literalinclude:: ../../../examples/wedding.py
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
161
doc/source/CaseStudies/a_sudoku_problem.rst
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
A Sudoku Problem formulated as an LP
|
||||||
|
====================================
|
||||||
|
|
||||||
|
|
||||||
|
Problem Description
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
A `sudoku problem <http://en.wikipedia.org/wiki/Sudoku>`_ is a problem where there are is an incomplete 9x9 table of numbers which must be filled according
|
||||||
|
to several rules:
|
||||||
|
|
||||||
|
+ Within any of the 9 individual 3x3 boxes, each of the numbers 1 to 9 must be
|
||||||
|
found
|
||||||
|
+ Within any column of the 9x9 grid, each of the numbers 1 to 9 must be found
|
||||||
|
+ Within any row of the 9x9 grid, each of the numbers 1 to 9 must be found
|
||||||
|
|
||||||
|
On this page we will formulate the below problem from wikipedia to model using PuLP. Once created, our code will need little modification to solve any sudoku problem at all.
|
||||||
|
|
||||||
|
.. image:: images/wikisudokuproblem.jpg
|
||||||
|
|
||||||
|
Formulation
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Identify the Decision Variables
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
In order to formulate this problem as a linear program, we cannot simply create
|
||||||
|
a variable for each of the 81 squares between 1 and 9 representing the value in
|
||||||
|
that square. This is because in linear programming there is no "not equal to"
|
||||||
|
operator and so we cannot use the necessary constraints of no squares within a
|
||||||
|
box/row/column being equal in value to each other. Whilst we can ensure the sum
|
||||||
|
of all the values in a box/row/column equal 45, this will still result in many
|
||||||
|
solutions satisfying the 45 constraint but still with 2 of the same number in
|
||||||
|
the same box/row/column.
|
||||||
|
|
||||||
|
Instead, we must create 729 individual binary (0-1) problem variables. These
|
||||||
|
represent 9 problem variables per square for each of 81 squares, where the 9
|
||||||
|
variables each correspond to the number that might be in that square. The
|
||||||
|
binary nature of the variable says whether the existence of that number in that
|
||||||
|
square is true or false. Therefore, there can clearly be only 1 of the 9
|
||||||
|
variables for each square as true (1) and the other 8 must be false (0) since
|
||||||
|
only one number can be placed into any square. This will become more clear.
|
||||||
|
|
||||||
|
|
||||||
|
Formulate the Objective Function
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Interestingly, with sudoku there is no solution that is better than another
|
||||||
|
solution, since a solution by definition, must satisfy all the constraints.
|
||||||
|
Therefore, we are not really trying to minimise or maximise anything, we are
|
||||||
|
just trying to find the values on our variables that satisfy the constraints.
|
||||||
|
Therefore, whilst either :attr:`~pulp.LpMinimize` or :attr:`~pulp.LpMaximize` must be entered, it is not
|
||||||
|
important which. Similarly, the objective function can be anything, so in this
|
||||||
|
example it is simply zero.
|
||||||
|
That is we are trying to minimize zero, subject to our constraints (meeting the constraints being the important part)
|
||||||
|
|
||||||
|
Formulate the Constraints
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
These are simply the known constraints of a sudoku problem plus the constraints
|
||||||
|
on our own created variables we have used to express the features of the
|
||||||
|
problem:
|
||||||
|
|
||||||
|
+ The values in the squares in any row must be each of 1 to 9
|
||||||
|
+ The values in the squares in any column must be each of 1 to 9
|
||||||
|
+ The values in the squares in any box must be each of 1 to 9
|
||||||
|
(a box is one of the 9 non-overlapping 3x3 grids within the overall 9x9 grid)
|
||||||
|
+ There must be only one number within any square (seems logically obvious, but
|
||||||
|
it is important to our formulation to ensure because of our variable choices)
|
||||||
|
+ The starting sudoku numbers must be in those same places in the final
|
||||||
|
solution (this is a constraint since these numbers are not changeable in the
|
||||||
|
actual problem, whereas we can control any other numbers. If none or very few
|
||||||
|
starting numbers were present, the sudoku problem would have a very large
|
||||||
|
number of feasible solutions, instead of just one)
|
||||||
|
|
||||||
|
|
||||||
|
Solution
|
||||||
|
--------
|
||||||
|
|
||||||
|
The introductory commenting and import statement are entered
|
||||||
|
|
||||||
|
.. literalinclude:: ../../../examples/Sudoku1.py
|
||||||
|
:lines: 1-8
|
||||||
|
|
||||||
|
In the unique case of the sudoku problem, the row names, column names and variable option values are all the exact same list of numbers (as strings) from "1" to "9".
|
||||||
|
|
||||||
|
.. literalinclude:: ../../../examples/Sudoku1.py
|
||||||
|
:lines: 10-16
|
||||||
|
|
||||||
|
|
||||||
|
A list called `Boxes` is created with 9 elements, each being another list. These 9 lists correspond to each of the 9 boxes, and each of the lists contains tuples as the elements with the row and column indices for each square in that box. Explicitly entering the values in a similar way to the following would have had the same effect (but would have been a waste of time):
|
||||||
|
|
||||||
|
.. literalinclude:: ../../../examples/Sudoku1.py
|
||||||
|
:lines: 18-22
|
||||||
|
|
||||||
|
Therefore, Boxes[0] will return a list of tuples containing the locations of each of the 9 squares in the first box.
|
||||||
|
|
||||||
|
The prob variable is created to contain the problem data. LpMinimize has the same effect as LpMaximise in this case.
|
||||||
|
|
||||||
|
.. literalinclude:: ../../../examples/Sudoku1.py
|
||||||
|
:lines: 24-25
|
||||||
|
|
||||||
|
|
||||||
|
The 729 problem variables are created since the `(Vals,Rows,Cols)` creates a variable for each combination of value, row and column. An example variable would be: Choice_4_2_9, and it is defined to be a binary variable (able to take only the integers 1 or 0. If Choice_4_2_9 was 1, it would mean the number 4 was present in the square situated in row 2, column 9. (If it was 0, it would mean there was not a 4 there)
|
||||||
|
|
||||||
|
.. literalinclude:: ../../../examples/Sudoku1.py
|
||||||
|
:lines: 27-28
|
||||||
|
|
||||||
|
As explained above, the objective function (what we try to change using the problem variables) is simply zero (constant) since we are only concerned with any variable combination that can satisfy the constraints.
|
||||||
|
|
||||||
|
.. literalinclude:: ../../../examples/Sudoku1.py
|
||||||
|
:lines: 30-31
|
||||||
|
|
||||||
|
Since there are 9 variables for each square, it is important to specify that only exactly one of them can take the value of "1" (and the rest are "0"). Therefore, the below code reads: for each of the 81 squares, the sum of all the 9 variables (each representing a value that could be there) relating to that particular square must equal 1.
|
||||||
|
|
||||||
|
.. literalinclude:: ../../../examples/Sudoku1.py
|
||||||
|
:lines: 33-36
|
||||||
|
|
||||||
|
These constraints ensure that each number (value) can only occur once in each row, column and box.
|
||||||
|
|
||||||
|
.. literalinclude:: ../../../examples/Sudoku1.py
|
||||||
|
:lines: 38-47
|
||||||
|
|
||||||
|
The starting numbers are entered as constraints i.e a "5" in row "1" column "1" is true.
|
||||||
|
|
||||||
|
.. literalinclude:: ../../../examples/Sudoku1.py
|
||||||
|
:lines: 49-78
|
||||||
|
|
||||||
|
The problem is written to an LP file, solved using CPLEX (due to CPLEX's simple output) and the solution status is printed to the screen
|
||||||
|
|
||||||
|
.. literalinclude:: ../../../examples/Sudoku1.py
|
||||||
|
:lines: 80-87
|
||||||
|
|
||||||
|
|
||||||
|
Instead of printing out all 729 of the binary problem variables and their respective values, it is most meaningful to draw the solution in a text file. The code also puts lines inbetween every third row and column to make the solution easier to read. The sudokuout.txt file is created in the same folder as the .py file.
|
||||||
|
|
||||||
|
.. literalinclude:: ../../../examples/Sudoku1.py
|
||||||
|
:lines: 92-108
|
||||||
|
|
||||||
|
A note of the location of the solution is printed to the solution
|
||||||
|
|
||||||
|
.. literalinclude:: ../../../examples/Sudoku1.py
|
||||||
|
:lines: 110-111
|
||||||
|
|
||||||
|
|
||||||
|
The full file above is given provided `Sudoku1.py <https://projects.coin-or.org/PuLP/browser/trunk/examples/Sudoku1.py?format=txt>`_
|
||||||
|
|
||||||
|
The final solution should be the following:
|
||||||
|
|
||||||
|
.. image:: images/wikisudokusolution.jpg
|
||||||
|
|
||||||
|
Extra for Experts
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
In the above formulation we did not consider the fact that there may be multiple solutions if the sudoku problem is not well defined.
|
||||||
|
|
||||||
|
We can make our code return all the solutions by editing our code as shown after the `prob.writeLP` line. Essentially we are just looping over the solve statement, and each time after a successful solve, adding a constraint that the same solution cannot be used again. When there are no more solutions, our program ends.
|
||||||
|
|
||||||
|
.. literalinclude:: ../../../examples/Sudoku2.py
|
||||||
|
:lines: 82-115
|
||||||
|
|
||||||
|
The full file using this is available `Sudoku2.py <https://projects.coin-or.org/PuLP/browser/trunk/examples/Sudoku2.py?format=txt>`_. When using this code for sudoku problems with a large number of solutions, it could take a very long time to solve them all. To create sudoku problems with multiple solutions from unique solution sudoku problem, you can simply delete a starting number constraint. You may find that deleting several constraints will still lead to a single optimal solution but the removal of one particular constraint leads to a sudden dramatic increase in the number of solutions.
|
||||||
422
doc/source/CaseStudies/a_transportation_problem.rst
Normal file
@@ -0,0 +1,422 @@
|
|||||||
|
A Transportation Problem
|
||||||
|
========================
|
||||||
|
|
||||||
|
Problem Description
|
||||||
|
-------------------
|
||||||
|
A boutique brewery has two warehouses from which it distributes beer to five
|
||||||
|
carefully chosen bars. At the start of every week, each bar sends an order to
|
||||||
|
the brewery’s head office for so many crates of beer, which is then dispatched
|
||||||
|
from the appropriate warehouse to the bar. The brewery would like to have an
|
||||||
|
interactive computer program which they can run week by week to tell them which
|
||||||
|
warehouse should supply which bar so as to minimize the costs of the whole
|
||||||
|
operation. For example, suppose that at the start of a given week the brewery
|
||||||
|
has 1000 cases at warehouse A, and 4000 cases at warehouse B, and that the bars
|
||||||
|
require 500, 900, 1800, 200, and 700 cases respectively. Which warehouse should
|
||||||
|
supply which bar?
|
||||||
|
|
||||||
|
Formulation
|
||||||
|
-----------
|
||||||
|
For transportation problems, using a graphical representation of the problem
|
||||||
|
is often helpful during formulation. Here is a graphical representation of The
|
||||||
|
Beer Distribution Problem.
|
||||||
|
|
||||||
|
.. image:: images/brewery_nodes.jpg
|
||||||
|
|
||||||
|
Identify the Decision Variables
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
In transportation problems we are deciding how to transport goods from their
|
||||||
|
supply nodes to their demand nodes. The decision variables are the Arcs
|
||||||
|
connecting these nodes, as shown in the diagram below. We are deciding how many
|
||||||
|
crates of beer to transport from each warehouse to each pub.
|
||||||
|
|
||||||
|
.. image:: images/brewery_arcs.jpg
|
||||||
|
|
||||||
|
* A1 = number of crates of beer to ship from Warehouse A to Bar 1
|
||||||
|
* A5 = number of crates of beer to ship from Warehouse A to Bar 5
|
||||||
|
* B1 = number of crates of beer to ship from Warehouse B to Bar 1
|
||||||
|
* B5 = number of crates of beer to ship from Warehouse B to Bar 5
|
||||||
|
|
||||||
|
Let:
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
W &= \{A,B\} \\
|
||||||
|
B &= \{1, 2, 3, 4, 5 \} \\
|
||||||
|
x_{(w,b)} &\ge 0 \ldots \forall w \in W, b \in B \\
|
||||||
|
x_{(w,b)} & \in \mathbb{Z}^+ \ldots \forall w \in W, b \in B \\
|
||||||
|
|
||||||
|
The lower bound on the variables is Zero, and the values must all be Integers
|
||||||
|
(since the number of crates cannot be negative or fractional). There is no
|
||||||
|
upper bound.
|
||||||
|
|
||||||
|
Formulate the Objective Function
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
The objective function has been loosely defined as cost. The problem can only be
|
||||||
|
formulated as a linear program if the cost of transportation from warehouse to
|
||||||
|
pub is a linear function of the amounts of crates transported. Note that this is
|
||||||
|
sometimes not the case. This may be due to factors such as economies of scale or
|
||||||
|
fixed costs. For example, transporting 10 crates may not cost 10 times as much
|
||||||
|
as transporting one crate, since it may be the case that one truck can
|
||||||
|
accommodate 10 crates as easily as one. Usually in this situation there are
|
||||||
|
fixed costs in operating a truck which implies that the costs go up in jumps
|
||||||
|
(when an extra truck is required). In these situations, it is possible to model
|
||||||
|
such a cost by using zero-one integer variables: we will look at this later in
|
||||||
|
the course.
|
||||||
|
|
||||||
|
We shall assume then that there is a fixed transportation cost per crate. (If
|
||||||
|
the capacity of a truck is small compared with the number of crates that must be
|
||||||
|
delivered then this is a valid assumption). Lets assume we talk with the
|
||||||
|
financial manager, and she gives us the following transportation costs (dollars
|
||||||
|
per crate):
|
||||||
|
|
||||||
|
======================= === ===
|
||||||
|
From Warehouse to Bar A B
|
||||||
|
======================= === ===
|
||||||
|
1 2 3
|
||||||
|
2 4 1
|
||||||
|
3 5 3
|
||||||
|
4 2 2
|
||||||
|
5 1 3
|
||||||
|
======================= === ===
|
||||||
|
|
||||||
|
Minimise the Transporting Costs = Cost per crate for RouteA1 * A1 (number of crates on RouteA1)
|
||||||
|
\+ ...
|
||||||
|
\+ Cost per crate for RouteB5 * B5 (number of crates on RouteB5)
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
\min \sum_{w \in W, b \in B} c_{(w,b)} x_{(w,b)}
|
||||||
|
|
||||||
|
Formulate the Constraints
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
The constraints come from considerations of supply and demand. The supply of
|
||||||
|
beer at warehouse A is 1000 cases. The total amount of beer shipped from
|
||||||
|
warehouse A cannot exceed this amount. Similarly, the amount of beer shipped
|
||||||
|
from warehouse B cannot exceed the supply of beer at warehouse B. The sum of
|
||||||
|
the values on all the arcs leading out of a warehouse, must be less than or
|
||||||
|
equal to the supply value at that warehouse:
|
||||||
|
|
||||||
|
Such that:
|
||||||
|
|
||||||
|
* A1 + A2 + A3 + A4 + A5 <= 1000
|
||||||
|
* B1 + B2 + B3 + B4 + B5 <= 4000
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
\sum_{ b \in B} x_{(w,b)} <= s_w \ldots \forall w \in W
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
The demand for beer at bar 1 is 500 cases, so the amount of beer delivered there
|
||||||
|
must be at least 500 to avoid lost sales. Similarly, considering the amounts
|
||||||
|
delivered to the other bars must be at least equal to the demand at those bars.
|
||||||
|
Note, we are assuming there are no penalties for oversupplying bars (other than
|
||||||
|
the extra transportation cost we incur). We can _balance_ the transportation
|
||||||
|
problem to make sure that demand is met exactly - there will be more on this
|
||||||
|
later. For now, the sum of the values on all the arcs leading into a bar, must
|
||||||
|
be greater than or equal to the demand value at that bar:
|
||||||
|
|
||||||
|
* A1 + B1 >= 500
|
||||||
|
* A2 + B2 >= 900
|
||||||
|
* A3 + B3 >= 1800
|
||||||
|
* A4 + B4 >= 200
|
||||||
|
* A5 + B5 >= 700
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
\sum_{ w \in W} x_{(w,b)} >= d_b \ldots \forall b \in B
|
||||||
|
|
||||||
|
Finally, we have already specified the amount of beer shipped must be
|
||||||
|
non-negative.
|
||||||
|
|
||||||
|
PuLP Model
|
||||||
|
----------
|
||||||
|
|
||||||
|
Whilst the LP as defined above could be formulated into Python code in the same
|
||||||
|
way as the `A Blending Problem` (Whiskas), for Transportation Problems, there is
|
||||||
|
a more efficient way which we will use in this course. The example file for this
|
||||||
|
problem is found in the examples directory BeerDistributionProblem.py
|
||||||
|
|
||||||
|
First, start your Python file with a heading and the import PuLP statement:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
"""
|
||||||
|
The Beer Distribution Problem for the PuLP Modeller
|
||||||
|
|
||||||
|
Authors: Antony Phillips, Dr Stuart Mitchell 2007
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Import PuLP modeller functions
|
||||||
|
from pulp import *
|
||||||
|
|
||||||
|
The start of the formulation is a simple definition of the nodes and their limits/capacities. The node names are put into lists, and their associated capacities are put into dictionaries with the node names as the reference keys:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
# Creates a list of all the supply nodes
|
||||||
|
Warehouses = ["A","B"]
|
||||||
|
|
||||||
|
# Creates a dictionary for the number of units of supply for each supply node
|
||||||
|
supply = {"A": 1000,
|
||||||
|
"B": 4000}
|
||||||
|
|
||||||
|
# Creates a list of all demand nodes
|
||||||
|
Bars = ["1", "2", "3", "4", "5"]
|
||||||
|
|
||||||
|
# Creates a dictionary for the number of units of demand for each demand node
|
||||||
|
demand = {"1": 500,
|
||||||
|
"2": 900,
|
||||||
|
"3": 1800,
|
||||||
|
"4": 200,
|
||||||
|
"5": 700}
|
||||||
|
|
||||||
|
The cost data is then inputted into a list, with two sub lists: the first
|
||||||
|
containing the costs of shipping from Warehouse A, and the second containing the
|
||||||
|
costs of shipping from Warehouse B. Note here that the commenting and structure
|
||||||
|
of the code makes the data appear as a table for easy editing. The `Warehouses`
|
||||||
|
and `Bars` lists (Supply and Demand nodes) are added to make a large list (of
|
||||||
|
all nodes) and inputted into PuLPs `makeDict` function. The second parameter is
|
||||||
|
the costs list as was previously created, and the last parameter sets the
|
||||||
|
default value for an arc cost. Once the cost dictionary is created, if
|
||||||
|
`costs["A"]["1"]` is called, it will return the cost of transporting from
|
||||||
|
warehouse A to bar 1, 2. If `costs["C"]["2"]` is called, it will return 0, since
|
||||||
|
this is the defined default.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
# Creates a list of costs of each transportation path
|
||||||
|
costs = [ #Bars
|
||||||
|
#1 2 3 4 5
|
||||||
|
[2,4,5,2,1],#A Warehouses
|
||||||
|
[3,1,3,2,3] #B
|
||||||
|
]
|
||||||
|
}}}
|
||||||
|
|
||||||
|
The `prob` variable is created using the `LpProblem` function, with the usual
|
||||||
|
input parameters.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
# Creates the prob variable to contain the problem data
|
||||||
|
prob = LpProblem("Beer Distribution Problem",LpMinimize)
|
||||||
|
|
||||||
|
A list of tuples is created containing all the arcs.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
# Creates a list of tuples containing all the possible routes for transport
|
||||||
|
Routes = [(w,b) for w in Warehouses for b in Bars]
|
||||||
|
|
||||||
|
A dictionary called `route_var` is created which contains the LP variables. The
|
||||||
|
reference keys to the dictionary are the warehouse name, then the bar
|
||||||
|
name(`["A"]["2"]`) , and the data is `Route_Tuple`. (e.g. `["A"]["2"]`:
|
||||||
|
Route_A_2). The lower limit of zero is set, the upper limit of `None` is set,
|
||||||
|
and the variables are defined to be Integers.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
# A dictionary called route_vars is created to contain the referenced variables (the routes)
|
||||||
|
route_vars = LpVariable.dicts("Route",(Warehouses,Bars),0,None,LpInteger)
|
||||||
|
|
||||||
|
The objective function is added to the variable `prob` using a list
|
||||||
|
comprehension. Since `route_vars` and `costs` are now dictionaries (with further
|
||||||
|
internal dictionaries), they can be used as if they were tables, as `for (w,b)
|
||||||
|
in Routes` will cycle through all the combinations/arcs. Note that `i` and `j`
|
||||||
|
could have been used, but `w` and `b` are more meaningful.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
# The objective function is added to prob first
|
||||||
|
prob += lpSum([route_vars[w][b]*costs[w][b] for (w,b) in Routes]), "Sum of Transporting Costs"
|
||||||
|
|
||||||
|
The supply and demand constraints are added using a normal `for` loop and a list
|
||||||
|
comprehension. Supply Constraints: For each warehouse in turn, the values of the
|
||||||
|
decision variables (number of beer cases on arc) to each of the bars is summed,
|
||||||
|
and then constrained to being less than or equal to the supply max for that
|
||||||
|
warehouse. Demand Constraints: For each bar in turn, the values of the decision
|
||||||
|
variables (number on arc) from each of the warehouses is summed, and then
|
||||||
|
constrained to being greater than or equal to the demand minimum.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
# The supply maximum constraints are added to prob for each supply node (warehouse)
|
||||||
|
for w in Warehouses:
|
||||||
|
prob += lpSum([route_vars[w][b] for b in Bars]) <= supply[w], "Sum of Products out of Warehouse %s"%w
|
||||||
|
|
||||||
|
# The demand minimum constraints are added to prob for each demand node (bar)
|
||||||
|
for b in Bars:
|
||||||
|
prob += lpSum([route_vars[w][b] for w in Warehouses]) >= demand[b], "Sum of Products into Bars %s"%b
|
||||||
|
|
||||||
|
Following this is the `prob.writeLP` line, and the rest as explained in previous
|
||||||
|
examples.
|
||||||
|
|
||||||
|
You will notice that the linear programme solution was also an integer solution.
|
||||||
|
As long as Supply and Demand are integers, the linear programming solution will
|
||||||
|
always be an integer. Read about naturally integer solutions for more details.
|
||||||
|
|
||||||
|
Extensions
|
||||||
|
----------
|
||||||
|
|
||||||
|
Validation
|
||||||
|
~~~~~~~~~~
|
||||||
|
|
||||||
|
Since we have guaranteed the Supply and Demand are integer, we know that the
|
||||||
|
solution to the linear programme will be integer, so we don't need to check the
|
||||||
|
integrality of the solution.
|
||||||
|
|
||||||
|
Storage and "Buying In"
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Transportation models are usually _balanced_, i.e., the total supply = the total
|
||||||
|
demand. This is because extra supply usually must be stored somewhere (with an
|
||||||
|
associated storage cost) and extra demand is usually satisfied by purchasing
|
||||||
|
extra goods from alternative sources (this is know as "buying in" extra goods)
|
||||||
|
or by substituting another product (incurring a penalty cost).
|
||||||
|
|
||||||
|
In The Beer Distribution Problem, the total supply is 5000 cases of beer, but
|
||||||
|
the total demand is only for 4100 cases. The extra supply can be sent to an
|
||||||
|
_dummy_ demand node. The cost of flow going to the dummy demand node is then the
|
||||||
|
storage cost at each of the supply nodes.
|
||||||
|
|
||||||
|
.. image:: images/extra_supply.jpg
|
||||||
|
|
||||||
|
This is added into the above model very simply. Since the objective function and
|
||||||
|
constraints all operated on the original supply, demand and cost
|
||||||
|
lists/dictionaries, the only changes that must be made to include another demand node are:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
# Creates a list of all demand nodes
|
||||||
|
Bars = ["1","2","3","4","5","D"]
|
||||||
|
|
||||||
|
# Creates a dictionary for the number of units of demand for each demand node
|
||||||
|
demand = {"1": 500,
|
||||||
|
"2": 900,
|
||||||
|
"3": 1800,
|
||||||
|
"4": 200,
|
||||||
|
"5": 700,
|
||||||
|
"D": 900}
|
||||||
|
|
||||||
|
# Creates a list of costs of each transportation path
|
||||||
|
costs = [ #Bars
|
||||||
|
#1 2 3 4 5 D
|
||||||
|
[2,4,5,2,1,0],#A Warehouses
|
||||||
|
[3,1,3,2,3,0] #B
|
||||||
|
]
|
||||||
|
|
||||||
|
The `Bars` list is expanded and the `Demand` dictionary is expanded to make the
|
||||||
|
Dummy Demand require 900 crates, to balance the problem. The cost list is also
|
||||||
|
expanded, to show the cost of "sending to the Dummy Node" which is realistically
|
||||||
|
just leaving the stock at the warehouses. This may have an associated cost which
|
||||||
|
could be entered here instead of the zeros. Note that the solution could still
|
||||||
|
be solved when there was an unbalanced excess supply.
|
||||||
|
|
||||||
|
If a transportation problem has more demand than supply, we can balance the
|
||||||
|
problem using a dummy supply node. Note that with excess demand, the problem is
|
||||||
|
"Infeasible" when unbalanced.
|
||||||
|
|
||||||
|
Assume there has been a production problem and only 4000 cases of beer could be
|
||||||
|
produced. Since the total demand is 4100, we need to get extra cases of beer from the dummy supply node.
|
||||||
|
|
||||||
|
|
||||||
|
.. image:: images/extra_demand.jpg
|
||||||
|
|
||||||
|
|
||||||
|
This dummy supply node is added in simply and logically into the `Warehouse`
|
||||||
|
list, `Supply` dictionary, and `costs` list. The Supply value is chosen to
|
||||||
|
balance the problem, and cost of transport is zero to all demand nodes.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
# Creates a list of all the supply nodes
|
||||||
|
Warehouses = ["A","B","D"]
|
||||||
|
|
||||||
|
# Creates a dictionary for the number of units of supply for each supply node
|
||||||
|
supply = {"A": 1000,
|
||||||
|
"B": 3000,
|
||||||
|
"D": 100}
|
||||||
|
|
||||||
|
# Creates a list of all demand nodes
|
||||||
|
Bars = ["1","2","3","4","5"]
|
||||||
|
|
||||||
|
# Creates a dictionary for the number of units of demand for each demand node
|
||||||
|
demand = {"1": 500,
|
||||||
|
"2": 900,
|
||||||
|
"3": 1800,
|
||||||
|
"4": 200,
|
||||||
|
"5": 700}
|
||||||
|
|
||||||
|
# Creates a list of costs of each transportation path
|
||||||
|
costs = [ #Bars
|
||||||
|
#1 2 3 4 5 D
|
||||||
|
[2,4,5,2,1], #A
|
||||||
|
[3,1,3,2,3], #B Warehouses
|
||||||
|
[0,0,0,0,0] #D
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
Presentation of Solution and Analysis
|
||||||
|
-------------------------------------
|
||||||
|
|
||||||
|
There are many ways to present the solution to The Beer Distribution Problem:
|
||||||
|
as a list of shipments, in a table, etc.
|
||||||
|
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
TRANSPORTATION SOLUTION -- Non-zero shipments
|
||||||
|
TotalCost = ____
|
||||||
|
|
||||||
|
Ship ___ crates of beer from warehouse A to pub 1
|
||||||
|
Ship ___ crates of beer from warehouse A to pub 5
|
||||||
|
Ship ___ crates of beer from warehouse B to pub 1
|
||||||
|
Ship ___ crates of beer from warehouse B to pub 2
|
||||||
|
Ship ___ crates of beer from warehouse B to pub 3
|
||||||
|
Ship ___ crates of beer from warehouse B to pub 4
|
||||||
|
|
||||||
|
This information gives rise to the following management summary:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
The Beer Distribution Problem
|
||||||
|
Mike O'Sullivan, 1234567
|
||||||
|
We are minimising the transportation cost for a brewery operation. The brewery
|
||||||
|
transports cases of beer from its warehouses to several bars.
|
||||||
|
|
||||||
|
The brewery has two warehouses (A and B respectively) and 5 bars (1, 2, 3, 4 and
|
||||||
|
5).
|
||||||
|
|
||||||
|
The supply of crates of beer at the warehouses is:
|
||||||
|
|
||||||
|
__________
|
||||||
|
|
||||||
|
The forecasted demand (in crates of beer) at the bars is:
|
||||||
|
|
||||||
|
__________
|
||||||
|
|
||||||
|
The cost of transporting 1 crate of beer from a warehouse to a bar is given in
|
||||||
|
the following table:
|
||||||
|
|
||||||
|
__________
|
||||||
|
|
||||||
|
To minimise the transportation cost the brewery should make the following
|
||||||
|
shipments:
|
||||||
|
|
||||||
|
Ship ___ crates of beer from warehouse A to pub 1
|
||||||
|
Ship ___ crates of beer from warehouse A to pub 5
|
||||||
|
Ship ___ crates of beer from warehouse B to pub 1
|
||||||
|
Ship ___ crates of beer from warehouse B to pub 2
|
||||||
|
Ship ___ crates of beer from warehouse B to pub 3
|
||||||
|
Ship ___ crates of beer from warehouse B to pub 4
|
||||||
|
|
||||||
|
The total transportation cost of this shipping schedule is $_____.
|
||||||
|
|
||||||
|
Ongoing Monitoring
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Ongoing Monitoring may take the form of:
|
||||||
|
|
||||||
|
* Updating your data files and resolving as the data changes (changing costs, supplies, demands);
|
||||||
|
* Resolving our model for new nodes (e.g., new warehouses or bars);
|
||||||
|
* Looking to see if cheaper transportation options are available along routes where the transportation cost affects the optimal solution (e.g., how much total savings can we get by reducing the transportation cost from Warehouse B to Bar 1).
|
||||||
BIN
doc/source/CaseStudies/images/brewery_arcs.jpg
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
doc/source/CaseStudies/images/brewery_nodes.jpg
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
doc/source/CaseStudies/images/extra_demand.jpg
Normal file
|
After Width: | Height: | Size: 147 KiB |
BIN
doc/source/CaseStudies/images/extra_supply.jpg
Normal file
|
After Width: | Height: | Size: 148 KiB |
BIN
doc/source/CaseStudies/images/wedding_seating.jpg
Normal file
|
After Width: | Height: | Size: 288 KiB |
BIN
doc/source/CaseStudies/images/whiskas_blend.jpg
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
doc/source/CaseStudies/images/whiskas_label.jpg
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
doc/source/CaseStudies/images/wikisudokuproblem.jpg
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
doc/source/CaseStudies/images/wikisudokusolution.jpg
Normal file
|
After Width: | Height: | Size: 22 KiB |
18
doc/source/CaseStudies/index.rst
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
Case Studies
|
||||||
|
=====================
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
|
||||||
|
a_blending_problem
|
||||||
|
a_set_partitioning_problem
|
||||||
|
a_sudoku_problem
|
||||||
|
|
||||||
|
Indices and tables
|
||||||
|
==================
|
||||||
|
|
||||||
|
* :ref:`genindex`
|
||||||
|
* :ref:`modindex`
|
||||||
|
* :ref:`search`
|
||||||
BIN
doc/source/_static/freebound.jpg
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
doc/source/_static/freebound.pdf
Normal file
52
doc/source/_static/plotter.py
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
|
from matplotlib import rc
|
||||||
|
rc('text', usetex=True)
|
||||||
|
rc('font', family='serif')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def plot_interval(a,c,x_left, x_right,i, fbound):
|
||||||
|
lh = c*(1-a[0])
|
||||||
|
rh = c*(1+a[1])
|
||||||
|
x=arange(x_left, x_right+1)
|
||||||
|
y=0*x
|
||||||
|
arrow_r = Arrow(c,0, c*a[1],0,0.2)
|
||||||
|
arrow_l = Arrow(c,0,-c*a[0],0,0.2)
|
||||||
|
plot(x,y)
|
||||||
|
text((x_left+lh)/2.0,0.1,'freebound interval [%s, %s] is penalty-free' % (lh,rh))
|
||||||
|
text((x_left+lh)/2.0, 0.2, 'rhs=%s, %s' % (c, fbound))
|
||||||
|
cur_ax = gca()
|
||||||
|
cur_ax.add_patch(arrow_l)
|
||||||
|
cur_ax.add_patch(arrow_r)
|
||||||
|
axis([x_left,x_right,-0.1,0.3])
|
||||||
|
yticks([])
|
||||||
|
title('Elasticized constraint\_%s $C(x)= %s $' % (i, c))
|
||||||
|
|
||||||
|
figure()
|
||||||
|
subplots_adjust(hspace=0.5)
|
||||||
|
|
||||||
|
fbound = 'proportionFreeBound'
|
||||||
|
i=1
|
||||||
|
subplot(2,1,i)
|
||||||
|
a=[0.01,0.01]
|
||||||
|
c = 200
|
||||||
|
x_left = 0.97*c
|
||||||
|
x_right = 1.03*c
|
||||||
|
fb_string = '%s%s = %s' %(fbound,'', a[0])
|
||||||
|
plot_interval(a,c,x_left, x_right,i, fb_string)
|
||||||
|
|
||||||
|
i += 1
|
||||||
|
subplot(2,1,i)
|
||||||
|
a=[0.02, 0.05]
|
||||||
|
c = 500
|
||||||
|
x_left = 0.9*c #scale of window
|
||||||
|
x_right = 1.2*c #scale of window
|
||||||
|
fb_string = '%s%s = [%s,%s]' % (fbound,'List', a[0],a[1])
|
||||||
|
plot_interval(a,c,x_left, x_right,i, fb_string)
|
||||||
|
savefig('freebound.jpg')
|
||||||
|
savefig('freebound.pdf')
|
||||||
|
|
||||||
|
# vim: fenc=utf-8: ft=python:sw=4:et:nu:fdm=indent:fdn=1:syn=python
|
||||||
|
|
||||||
9
doc/source/_templates/layout.html
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{% extends '!layout.html' %}
|
||||||
|
|
||||||
|
{% block footer %}
|
||||||
|
{{ super() }}
|
||||||
|
<center>
|
||||||
|
<a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/nz/"><img alt="Creative Commons License" style="border-width:0" src="http://i.creativecommons.org/l/by-sa/3.0/nz/88x31.png" /></a><br /><span xmlns:dc="http://purl.org/dc/elements/1.1/" href="http://purl.org/dc/dcmitype/Text" property="dc:title" rel="dc:type">PuLP documentation</span> by <span xmlns:cc="http://creativecommons.org/ns#" property="cc:attributionName">Pulp documentation team</span> is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/nz/">Creative Commons Attribution-Share Alike 3.0 New Zealand License</a>.
|
||||||
|
</center>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
202
doc/source/conf.py
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# pulp sphinx documentation build configuration file, created by
|
||||||
|
# sphinx-quickstart on Mon Nov 2 11:54:01 2009.
|
||||||
|
#
|
||||||
|
# This file is execfile()d with the current directory set to its containing dir.
|
||||||
|
#
|
||||||
|
# Note that not all possible configuration values are present in this
|
||||||
|
# autogenerated file.
|
||||||
|
#
|
||||||
|
# All configuration values have a default; values that are commented out
|
||||||
|
# serve to show the default.
|
||||||
|
|
||||||
|
import sys, os
|
||||||
|
|
||||||
|
# If extensions (or modules to document with autodoc) are in another directory,
|
||||||
|
# add these directories to sys.path here. If the directory is relative to the
|
||||||
|
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||||
|
sys.path.insert(0,os.path.abspath('../src/pulp/'))
|
||||||
|
|
||||||
|
# -- General configuration -----------------------------------------------------
|
||||||
|
|
||||||
|
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||||
|
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||||
|
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.intersphinx', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.pngmath', 'sphinx.ext.ifconfig', 'sphinx.ext.autosummary']
|
||||||
|
|
||||||
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
|
templates_path = ['_templates']
|
||||||
|
|
||||||
|
pngmath_use_preview = True
|
||||||
|
todo_include_todos = True
|
||||||
|
|
||||||
|
autoclass_content = 'both'
|
||||||
|
# The suffix of source filenames.
|
||||||
|
source_suffix = '.rst'
|
||||||
|
|
||||||
|
# The encoding of source files.
|
||||||
|
#source_encoding = 'utf-8'
|
||||||
|
|
||||||
|
# The master toctree document.
|
||||||
|
master_doc = 'index'
|
||||||
|
|
||||||
|
# General information about the project.
|
||||||
|
project = u'PuLP'
|
||||||
|
copyright = u'2009-, pulp documentation team.'
|
||||||
|
|
||||||
|
# The version info for the project you're documenting, acts as replacement for
|
||||||
|
# |version| and |release|, also used in various other places throughout the
|
||||||
|
# built documents.
|
||||||
|
#
|
||||||
|
# The full version, including alpha/beta/rc tags.
|
||||||
|
release = open(os.path.join('..','..','VERSION')).read().strip()
|
||||||
|
# The short X.Y version.
|
||||||
|
version = release[:3]
|
||||||
|
|
||||||
|
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||||
|
# for a list of supported languages.
|
||||||
|
#language = None
|
||||||
|
|
||||||
|
# There are two options for replacing |today|: either, you set today to some
|
||||||
|
# non-false value, then it is used:
|
||||||
|
#today = ''
|
||||||
|
# Else, today_fmt is used as the format for a strftime call.
|
||||||
|
#today_fmt = '%B %d, %Y'
|
||||||
|
|
||||||
|
# List of documents that shouldn't be included in the build.
|
||||||
|
#unused_docs = []
|
||||||
|
|
||||||
|
# List of directories, relative to source directory, that shouldn't be searched
|
||||||
|
# for source files.
|
||||||
|
exclude_trees = []
|
||||||
|
|
||||||
|
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||||
|
#default_role = None
|
||||||
|
|
||||||
|
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||||
|
add_function_parentheses = True
|
||||||
|
|
||||||
|
# If true, the current module name will be prepended to all description
|
||||||
|
# unit titles (such as .. function::).
|
||||||
|
#add_module_names = True
|
||||||
|
|
||||||
|
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||||
|
# output. They are ignored by default.
|
||||||
|
#show_authors = False
|
||||||
|
|
||||||
|
# The name of the Pygments (syntax highlighting) style to use.
|
||||||
|
pygments_style = 'sphinx'
|
||||||
|
|
||||||
|
# A list of ignored prefixes for module index sorting.
|
||||||
|
#modindex_common_prefix = []
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for HTML output ---------------------------------------------------
|
||||||
|
|
||||||
|
# The theme to use for HTML and HTML Help pages. Major themes that come with
|
||||||
|
# Sphinx are currently 'default' and 'sphinxdoc'.
|
||||||
|
html_theme = 'default'
|
||||||
|
|
||||||
|
# Theme options are theme-specific and customize the look and feel of a theme
|
||||||
|
# further. For a list of options available for each theme, see the
|
||||||
|
# documentation.
|
||||||
|
#html_theme_options = {}
|
||||||
|
|
||||||
|
# Add any paths that contain custom themes here, relative to this directory.
|
||||||
|
#html_theme_path = []
|
||||||
|
|
||||||
|
# The name for this set of Sphinx documents. If None, it defaults to
|
||||||
|
# "<project> v<release> documentation".
|
||||||
|
#html_title = None
|
||||||
|
|
||||||
|
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||||
|
#html_short_title = None
|
||||||
|
|
||||||
|
# The name of an image file (relative to this directory) to place at the top
|
||||||
|
# of the sidebar.
|
||||||
|
#html_logo = None
|
||||||
|
|
||||||
|
# The name of an image file (within the static path) to use as favicon of the
|
||||||
|
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||||
|
# pixels large.
|
||||||
|
#html_favicon = None
|
||||||
|
|
||||||
|
# Add any paths that contain custom static files (such as style sheets) here,
|
||||||
|
# relative to this directory. They are copied after the builtin static files,
|
||||||
|
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||||
|
html_static_path = ['_static']
|
||||||
|
|
||||||
|
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||||
|
# using the given strftime format.
|
||||||
|
#html_last_updated_fmt = '%b %d, %Y'
|
||||||
|
|
||||||
|
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||||
|
# typographically correct entities.
|
||||||
|
#html_use_smartypants = True
|
||||||
|
|
||||||
|
# Custom sidebar templates, maps document names to template names.
|
||||||
|
#html_sidebars = {}
|
||||||
|
|
||||||
|
# Additional templates that should be rendered to pages, maps page names to
|
||||||
|
# template names.
|
||||||
|
#html_additional_pages = {}
|
||||||
|
|
||||||
|
# If false, no module index is generated.
|
||||||
|
#html_use_modindex = True
|
||||||
|
|
||||||
|
# If false, no index is generated.
|
||||||
|
#html_use_index = True
|
||||||
|
|
||||||
|
# If true, the index is split into individual pages for each letter.
|
||||||
|
#html_split_index = False
|
||||||
|
|
||||||
|
# If true, links to the reST sources are added to the pages.
|
||||||
|
#html_show_sourcelink = True
|
||||||
|
|
||||||
|
# If true, an OpenSearch description file will be output, and all pages will
|
||||||
|
# contain a <link> tag referring to it. The value of this option must be the
|
||||||
|
# base URL from which the finished HTML is served.
|
||||||
|
#html_use_opensearch = ''
|
||||||
|
|
||||||
|
# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
|
||||||
|
#html_file_suffix = ''
|
||||||
|
|
||||||
|
# Output file base name for HTML help builder.
|
||||||
|
htmlhelp_basename = 'pulpdoc'
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for LaTeX output --------------------------------------------------
|
||||||
|
|
||||||
|
# The paper size ('letter' or 'a4').
|
||||||
|
#latex_paper_size = 'letter'
|
||||||
|
|
||||||
|
# The font size ('10pt', '11pt' or '12pt').
|
||||||
|
#latex_font_size = '10pt'
|
||||||
|
|
||||||
|
# Grouping the document tree into LaTeX files. List of tuples
|
||||||
|
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||||
|
latex_documents = [
|
||||||
|
('index', 'pulp.tex', u'pulp Documentation',
|
||||||
|
u'pulp documentation team', 'manual'),
|
||||||
|
]
|
||||||
|
|
||||||
|
# The name of an image file (relative to this directory) to place at the top of
|
||||||
|
# the title page.
|
||||||
|
#latex_logo = None
|
||||||
|
|
||||||
|
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||||
|
# not chapters.
|
||||||
|
#latex_use_parts = False
|
||||||
|
|
||||||
|
# Additional stuff for the LaTeX preamble.
|
||||||
|
#latex_preamble = ''
|
||||||
|
|
||||||
|
# Documents to append as an appendix to all manuals.
|
||||||
|
#latex_appendices = []
|
||||||
|
|
||||||
|
# If false, no module index is generated.
|
||||||
|
#latex_use_modindex = True
|
||||||
|
|
||||||
|
|
||||||
|
# Example configuration for intersphinx: refer to the Python standard library.
|
||||||
|
intersphinx_mapping = {'http://docs.python.org/': None}
|
||||||
89
doc/source/constants.rst
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
|
||||||
|
:mod:`pulp.constants`
|
||||||
|
=====================
|
||||||
|
|
||||||
|
.. automodule:: pulp.constants
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:inherited-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
.. data:: LpStatus
|
||||||
|
|
||||||
|
Return status from solver:
|
||||||
|
|
||||||
|
+-----------------------------+---------------+-----------------+
|
||||||
|
| LpStatus key | string value | numerical value |
|
||||||
|
+=============================+===============+=================+
|
||||||
|
| :data:`LpStatusOptimal` | "Optimal" | 1 |
|
||||||
|
+-----------------------------+---------------+-----------------+
|
||||||
|
| :data:`LpStatusNotSolved` | "Not Solved" | 0 |
|
||||||
|
+-----------------------------+---------------+-----------------+
|
||||||
|
| :data:`LpStatusInfeasible` | "Infeasible" | -1 |
|
||||||
|
+-----------------------------+---------------+-----------------+
|
||||||
|
| :data:`LpStatusUnbounded` | "Unbounded" | -2 |
|
||||||
|
+-----------------------------+---------------+-----------------+
|
||||||
|
| :data:`LpStatusUndefined` | "Undefined" | -3 |
|
||||||
|
+-----------------------------+---------------+-----------------+
|
||||||
|
|
||||||
|
.. data:: LpStatusOptimal
|
||||||
|
|
||||||
|
LpStatusOptimal = 1
|
||||||
|
|
||||||
|
.. data:: LpStatusNotSolved
|
||||||
|
|
||||||
|
LpStatusNotSolved = 0
|
||||||
|
|
||||||
|
.. data:: LpStatusInfeasible
|
||||||
|
|
||||||
|
LpStatusInfeasible = -1
|
||||||
|
|
||||||
|
.. data:: LpStatusUnbounded
|
||||||
|
|
||||||
|
LpStatusUnbounded = -2
|
||||||
|
|
||||||
|
.. data:: LpStatusUndefined
|
||||||
|
|
||||||
|
LpStatusUndefined = -3
|
||||||
|
|
||||||
|
|
||||||
|
.. data:: LpSenses
|
||||||
|
|
||||||
|
Dictionary of values for :attr:`~pulp.pulp.LpProblem.sense`:
|
||||||
|
|
||||||
|
LpSenses =
|
||||||
|
{:data:`LpMaximize`:"Maximize", :data:`LpMinimize`:"Minimize"}
|
||||||
|
|
||||||
|
.. data:: LpMinimize
|
||||||
|
|
||||||
|
LpMinimize = 1
|
||||||
|
|
||||||
|
.. data:: LpMaximize
|
||||||
|
|
||||||
|
LpMaximize = -1
|
||||||
|
|
||||||
|
.. data:: LpConstraintEQ
|
||||||
|
|
||||||
|
LpConstraintEQ = 0
|
||||||
|
|
||||||
|
.. data:: LpConstraintLE
|
||||||
|
|
||||||
|
LpConstraintLE = -1
|
||||||
|
|
||||||
|
.. data:: LpConstraintGE
|
||||||
|
|
||||||
|
LpConstraintGE = 1
|
||||||
|
|
||||||
|
.. data:: LpConstraintSenses
|
||||||
|
|
||||||
|
+--------------------------+----------------+-----------------+
|
||||||
|
| LpConstraint key | symbolic value | numerical value |
|
||||||
|
+==========================+================+=================+
|
||||||
|
| :data:`LpConstraintEQ` | "==" | 0 |
|
||||||
|
+--------------------------+----------------+-----------------+
|
||||||
|
| :data:`LpConstraintLE` | "<=" | -1 |
|
||||||
|
+--------------------------+----------------+-----------------+
|
||||||
|
| :data:`LpConstraintGE` | ">=" | 1 |
|
||||||
|
+--------------------------+----------------+-----------------+
|
||||||
|
|
||||||
|
|
||||||
62
doc/source/documentation/elastic.rst
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
Elastic Constraints
|
||||||
|
^^^^^^^^^^^^^^^^^^^
|
||||||
|
.. currentmodule:: pulp
|
||||||
|
|
||||||
|
A constraint :math:`C(x) = c` (equality may be replaced by :math:`\le`
|
||||||
|
or :math:`\ge`)
|
||||||
|
can be elasticized to the form
|
||||||
|
|
||||||
|
.. math:: C(x) \in D
|
||||||
|
|
||||||
|
where :math:`D` denotes some interval containing the value
|
||||||
|
:math:`c`.
|
||||||
|
|
||||||
|
Define the constraint in two steps:
|
||||||
|
|
||||||
|
#. instantiate constraint (subclass of :class:`LpConstraint`) with target :math:`c`.
|
||||||
|
#. call its :meth:`~LpConstraint.makeElasticSubProblem` method which returns
|
||||||
|
an object of type :class:`FixedElasticSubProblem`
|
||||||
|
(subclass of :class:`LpProblem`) - its objective is the minimization
|
||||||
|
of the distance of :math:`C(x)` from :math:`D`.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
constraint = LpConstraint(..., rhs = c)
|
||||||
|
elasticProblem = constraint.makeElasticSubProblem(
|
||||||
|
penalty = <penalty_value>,
|
||||||
|
proportionFreeBound = <freebound_value>,
|
||||||
|
proportionFreeBoundList = <freebound_list_value>,
|
||||||
|
)
|
||||||
|
|
||||||
|
where:
|
||||||
|
* ``<penalty_value>`` is a real number
|
||||||
|
* ``<freebound_value>`` :math:`a \in [0,1]` specifies a symmetric
|
||||||
|
target interval :math:`D = (c(1-a),c(1+a))` about :math:`c`
|
||||||
|
* ``<freebound_list_value> = [a,b]``, a list of
|
||||||
|
proportions :math:`a, b \in [0,1]` specifying an asymmetric target
|
||||||
|
interval :math:`D = (c(1-a),c(1+b))` about :math:`c`
|
||||||
|
|
||||||
|
The penalty applies to the constraint at points :math:`x` where
|
||||||
|
:math:`C(x) \not \in D`.
|
||||||
|
The magnitude of ``<penalty_value>`` can be assessed by examining
|
||||||
|
the final objective function in the ``.lp`` file written by
|
||||||
|
:meth:`LpProblem.writeLP`.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
>>> constraint_1 = LpConstraint('ex_1',sense=1,rhs=200)
|
||||||
|
>>> elasticProblem_1 = constraint_1.makeElasticSubproblem(penalty=1, proportionFreeBound = 0.01)
|
||||||
|
>>> constraint_2 = LpConstraint('ex_2',sense=0,rhs=500)
|
||||||
|
>>> elasticProblem_2 = constraint_2.makeElasticSubproblem(penalty=1,
|
||||||
|
proportionFreeBoundList = [0.02, 0.05])
|
||||||
|
|
||||||
|
#. constraint_1 has a penalty-free target interval of 1% either side of the rhs value, 200
|
||||||
|
#. constraint_2 has a penalty-free target interval of
|
||||||
|
- 2% on left and 5% on the right side of the rhs value, 500
|
||||||
|
|
||||||
|
.. image:: _static/freebound.*
|
||||||
|
:height: 5in
|
||||||
|
:alt: Freebound interval
|
||||||
|
|
||||||
|
Following are the methods of the return-value:
|
||||||
|
|
||||||
63
doc/source/index.rst
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
.. pulp_sphinx documentation master file, created by
|
||||||
|
sphinx-quickstart on Sun Nov 1 14:59:49 2009.
|
||||||
|
You can adapt this file completely to your liking, but it should at least
|
||||||
|
contain the root `toctree` directive.
|
||||||
|
|
||||||
|
Optimization with PuLP
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
You can begin learning Python and using PuLP
|
||||||
|
by looking at the content below. We recommend that you read The Optimisation Process,
|
||||||
|
Optimisation Concepts, and the Introduction to Python
|
||||||
|
before beginning the case-studies. For instructions for the installation of PuLP
|
||||||
|
see :ref:`installation`.
|
||||||
|
|
||||||
|
The full PuLP function documentation is available, and useful functions
|
||||||
|
will be explained in the case studies.
|
||||||
|
The case studies are in order, so the later case studies will assume you have
|
||||||
|
(at least) read the earlier case studies. However, we will provide links to any
|
||||||
|
relevant information you will need.
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
|
||||||
|
main/index
|
||||||
|
CaseStudies/index
|
||||||
|
.. comment block
|
||||||
|
main/Concepts
|
||||||
|
main/Introduction
|
||||||
|
main/Installing%20Python InstallingPythonatHome
|
||||||
|
main/BasicPythonCoding
|
||||||
|
main/Introduction to PuLP
|
||||||
|
main/InstallingPulpatHome
|
||||||
|
main/CaseStudies/ABLendingProblem
|
||||||
|
main/UsingElasticConstraints
|
||||||
|
main/Using FractionalConstraints
|
||||||
|
main/SequentialOptimisation
|
||||||
|
main/ExtraCaseStudies
|
||||||
|
|
||||||
|
PuLP Internal Documentation
|
||||||
|
===========================
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 1
|
||||||
|
|
||||||
|
constants
|
||||||
|
pulp
|
||||||
|
solvers
|
||||||
|
|
||||||
|
Authors
|
||||||
|
=======
|
||||||
|
|
||||||
|
The authors of this documentation (the pulp documentation team) include:
|
||||||
|
|
||||||
|
.. include:: AUTHORS.txt
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Indices and tables
|
||||||
|
==================
|
||||||
|
|
||||||
|
* :ref:`genindex`
|
||||||
|
* :ref:`modindex`
|
||||||
|
* :ref:`search`
|
||||||
|
|
||||||
531
doc/source/main/amply.rst
Normal file
@@ -0,0 +1,531 @@
|
|||||||
|
Amply
|
||||||
|
======
|
||||||
|
|
||||||
|
|
||||||
|
Introduction
|
||||||
|
------------
|
||||||
|
|
||||||
|
Amply allows you to load and manipulate AMPL data as Python data structures.
|
||||||
|
|
||||||
|
Amply only supports a specific subset of the AMPL syntax:
|
||||||
|
|
||||||
|
* set declarations
|
||||||
|
* set data statements
|
||||||
|
* parameter declarations
|
||||||
|
* parameter data statements
|
||||||
|
|
||||||
|
Declarations and data statements
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Typically, problems expressed in AMPL consist of two parts, a *model* section and a *data* section.
|
||||||
|
Amply is only designed to parse the parameter and set statements contained within AMPL data sections.
|
||||||
|
However, in order to parse these statements correctly, information that would usually be contained
|
||||||
|
within the model section may be required. For instance, it may not be possible to infer the dimension
|
||||||
|
of a set purely from its data statement. Therefore, Amply also supports set and parameter declarations.
|
||||||
|
These do not have to be put in a separate section, they only need to occur before the corresponding
|
||||||
|
data statement.
|
||||||
|
|
||||||
|
|
||||||
|
The declaration syntax supported is extremely limited, and does not include most
|
||||||
|
elements of the AMPL programming language. The intention is that this library
|
||||||
|
is used as a way of loading data specified in an AMPL-like syntax.
|
||||||
|
|
||||||
|
Furthermore, Amply does not perform any validation on data statements.
|
||||||
|
|
||||||
|
About this document
|
||||||
|
^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
This document is intended as a guide to the syntax supported by Amply, and not as a general
|
||||||
|
AMPL reference manual. For more in depth coverage see the `GNU MathProg manual, Chapter 5: Model data
|
||||||
|
<http://www.cs.unb.ca/~bremner/docs/glpk/gmpl.pdf>`_ or the following links:
|
||||||
|
|
||||||
|
* `Sets in AMPL <http://twiki.esc.auckland.ac.nz/twiki/bin/view/OpsRes/SetsInAMPL>`_
|
||||||
|
* `Parameters in AMPL <http://twiki.esc.auckland.ac.nz/twiki/bin/view/OpsRes/ParametersInAMPL>`_
|
||||||
|
|
||||||
|
Quickstart Guide
|
||||||
|
----------------
|
||||||
|
|
||||||
|
.. testsetup::
|
||||||
|
|
||||||
|
>>> from pulp import Amply
|
||||||
|
|
||||||
|
Import the class: ::
|
||||||
|
|
||||||
|
>>> from pulp import Amply
|
||||||
|
|
||||||
|
A simple set. Sets behave a lot like lists.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
|
||||||
|
>>> data = Amply("set CITIES := Auckland Wellington Christchurch;")
|
||||||
|
>>> print data.CITIES
|
||||||
|
<SetObject: ['Auckland', 'Wellington', 'Christchurch']>
|
||||||
|
>>> print data['CITIES']
|
||||||
|
<SetObject: ['Auckland', 'Wellington', 'Christchurch']>
|
||||||
|
>>> for c in data.CITIES: print c
|
||||||
|
...
|
||||||
|
Auckland
|
||||||
|
Wellington
|
||||||
|
Christchurch
|
||||||
|
>>> print data.CITIES[0]
|
||||||
|
Auckland
|
||||||
|
>>> print len(data.CITIES)
|
||||||
|
3
|
||||||
|
|
||||||
|
|
||||||
|
Data can be integers, reals, symbolic, or quoted strings:
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
|
||||||
|
>>> data = Amply("""
|
||||||
|
... set BitsNPieces := 0 3.2 -6e4 Hello "Hello, World!";
|
||||||
|
... """)
|
||||||
|
>>> print data.BitsNPieces
|
||||||
|
<SetObject: [0.0, 3.2000000000000002, -60000.0, 'Hello', 'Hello, World!']>
|
||||||
|
|
||||||
|
Sets can contain multidimensional data, but we have to declare them to be so first.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
|
||||||
|
>>> data = Amply("""
|
||||||
|
... set pairs dimen 2;
|
||||||
|
... set pairs := (1, 2) (2, 3) (3, 4);
|
||||||
|
... """)
|
||||||
|
>>> print data.pairs
|
||||||
|
<SetObject: [(1, 2), (2, 3), (3, 4)]>
|
||||||
|
|
||||||
|
Sets themselves can be multidimensional (i.e. be subscriptable):
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
|
||||||
|
>>> data = Amply("""
|
||||||
|
... set CITIES{COUNTRIES};
|
||||||
|
... set CITIES[Australia] := Adelaide Melbourne Sydney;
|
||||||
|
... set CITIES[Italy] := Florence Milan Rome;
|
||||||
|
... """)
|
||||||
|
>>> print data.CITIES['Australia']
|
||||||
|
['Adelaide', 'Melbourne', 'Sydney']
|
||||||
|
>>> print data.CITIES['Italy']
|
||||||
|
['Florence', 'Milan', 'Rome']
|
||||||
|
|
||||||
|
Note that in the above example, the set COUNTRIES didn't actually have to exist itself.
|
||||||
|
Amply does not perform any validation on subscripts, it only uses them to figure out
|
||||||
|
how many subscripts a set has. To specify more than one, separate them by commas:
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
|
||||||
|
>>> data = Amply("""
|
||||||
|
... set SUBURBS{COUNTRIES, CITIES};
|
||||||
|
... set SUBURBS[Australia, Melbourne] := Docklands 'South Wharf' Kensington;
|
||||||
|
... """)
|
||||||
|
>>> print data.SUBURBS['Australia', 'Melbourne']
|
||||||
|
['Docklands', 'South Wharf', 'Kensington']
|
||||||
|
|
||||||
|
*Slices* can be used to simplify the entry of multi-dimensional data.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
|
||||||
|
>>> data=Amply("""
|
||||||
|
... set TRIPLES dimen 3;
|
||||||
|
... set TRIPLES := (1, 1, *) 2 3 4 (*, 2, *) 6 7 8 9 (*, *, *) (1, 1, 1);
|
||||||
|
... """)
|
||||||
|
>>> print data.TRIPLES
|
||||||
|
<SetObject: [(1, 1, 2), (1, 1, 3), (1, 1, 4), (6, 2, 7), (8, 2, 9), (1, 1, 1)]>
|
||||||
|
>
|
||||||
|
|
||||||
|
Set data can also be specified using a matrix notation.
|
||||||
|
A '+' indicates that the pair is included in the set whereas a '-' indicates a
|
||||||
|
pair not in the set.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
|
||||||
|
>>> data=Amply("""
|
||||||
|
... set ROUTES dimen 2;
|
||||||
|
... set ROUTES : A B C D :=
|
||||||
|
... E + - - +
|
||||||
|
... F + + - -
|
||||||
|
... ;
|
||||||
|
... """)
|
||||||
|
>>> print data.ROUTES
|
||||||
|
<SetObject: [('E', 'A'), ('E', 'D'), ('F', 'A'), ('F', 'B')]>
|
||||||
|
|
||||||
|
Matrices can also be transposed:
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
|
||||||
|
>>> data=Amply("""
|
||||||
|
... set ROUTES dimen 2;
|
||||||
|
... set ROUTES (tr) : E F :=
|
||||||
|
... A + +
|
||||||
|
... B - +
|
||||||
|
... C - -
|
||||||
|
... D + -
|
||||||
|
... ;
|
||||||
|
... """)
|
||||||
|
>>> print data.ROUTES
|
||||||
|
<SetObject: [('E', 'A'), ('F', 'A'), ('F', 'B'), ('E', 'D')]>
|
||||||
|
|
||||||
|
Matrices only specify 2d data, however they can be combined with slices
|
||||||
|
to define higher-dimensional data:
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
|
||||||
|
>>> data = Amply("""
|
||||||
|
... set QUADS dimen 2;
|
||||||
|
... set QUADS :=
|
||||||
|
... (1, 1, *, *) : 2 3 4 :=
|
||||||
|
... 2 + - +
|
||||||
|
... 3 - + +
|
||||||
|
... (1, 2, *, *) : 2 3 4 :=
|
||||||
|
... 2 - + -
|
||||||
|
... 3 + - -
|
||||||
|
... ;
|
||||||
|
... """)
|
||||||
|
>>> print data.QUADS
|
||||||
|
<SetObject: [(1, 1, 2, 2), (1, 1, 2, 4), (1, 1, 3, 3), (1, 1, 3, 4), (1, 2, 2, 3), (1, 2, 3, 2)]>
|
||||||
|
|
||||||
|
Parameters are also supported:
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
|
||||||
|
>>> data = Amply("""
|
||||||
|
... param T := 30;
|
||||||
|
... param n := 5;
|
||||||
|
... """)
|
||||||
|
>>> print data.T
|
||||||
|
30
|
||||||
|
>>> print data.n
|
||||||
|
5
|
||||||
|
|
||||||
|
Parameters are commonly indexed over sets. No validation is done by Amply,
|
||||||
|
and the sets do not have to exist. Parameter objects are represented
|
||||||
|
as a mapping.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
|
||||||
|
>>> data = Amply("""
|
||||||
|
... param COSTS{PRODUCTS};
|
||||||
|
... param COSTS :=
|
||||||
|
... FISH 8.5
|
||||||
|
... CARROTS 2.4
|
||||||
|
... POTATOES 1.6
|
||||||
|
... ;
|
||||||
|
... """)
|
||||||
|
>>> print data.COSTS
|
||||||
|
<ParamObject: {'POTATOES': 1.6000000000000001, 'FISH': 8.5, 'CARROTS': 2.3999999999999999}>
|
||||||
|
>>> print data.COSTS['FISH']
|
||||||
|
8.5
|
||||||
|
|
||||||
|
Parameter data statements can include a *default* clause. If a '.' is included
|
||||||
|
in the data, it is replaced with the default value:
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
|
||||||
|
>>> data = Amply("""
|
||||||
|
... param COSTS{P};
|
||||||
|
... param COSTS default 2 :=
|
||||||
|
... F 2
|
||||||
|
... E 1
|
||||||
|
... D .
|
||||||
|
... ;
|
||||||
|
... """)
|
||||||
|
>>> print data.COSTS['D']
|
||||||
|
2.0
|
||||||
|
|
||||||
|
Parameter declarations can also have a default clause. For these parameters,
|
||||||
|
any attempt to access the parameter for a key that has not been defined
|
||||||
|
will return the default value:
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
|
||||||
|
>>> data = Amply("""
|
||||||
|
... param COSTS{P} default 42;
|
||||||
|
... param COSTS :=
|
||||||
|
... F 2
|
||||||
|
... E 1
|
||||||
|
... ;
|
||||||
|
... """)
|
||||||
|
>>> print data.COSTS['DOES NOT EXIST']
|
||||||
|
42.0
|
||||||
|
|
||||||
|
Parameters can be indexed over multiple sets. The resulting values can be
|
||||||
|
accessed by treating the parameter object as a nested dictionary, or by
|
||||||
|
using a tuple as an index:
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
|
||||||
|
>>> data = Amply("""
|
||||||
|
... param COSTS{CITIES, PRODUCTS};
|
||||||
|
... param COSTS :=
|
||||||
|
... Auckland FISH 5
|
||||||
|
... Auckland CHIPS 3
|
||||||
|
... Wellington FISH 4
|
||||||
|
... Wellington CHIPS 1
|
||||||
|
... ;
|
||||||
|
... """)
|
||||||
|
>>> print data.COSTS
|
||||||
|
<ParamObject: {'Wellington': {'FISH': 4.0, 'CHIPS': 1.0}, 'Auckland': {'FISH': 5.0, 'CHIPS': 3.0}}>
|
||||||
|
>>> print data.COSTS['Wellington']['CHIPS'] # nested dict
|
||||||
|
1.0
|
||||||
|
>>> print data.COSTS['Wellington', 'CHIPS'] # tuple as key
|
||||||
|
1.0
|
||||||
|
|
||||||
|
Parameters support a slice syntax similar to that of sets:
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
|
||||||
|
>>> data = Amply("""
|
||||||
|
... param COSTS{CITIES, PRODUCTS};
|
||||||
|
... param COSTS :=
|
||||||
|
... [Auckland, * ]
|
||||||
|
... FISH 5
|
||||||
|
... CHIPS 3
|
||||||
|
... [Wellington, * ]
|
||||||
|
... FISH 4
|
||||||
|
... CHIPS 1
|
||||||
|
... ;
|
||||||
|
... """)
|
||||||
|
>>> print data.COSTS
|
||||||
|
<ParamObject: {'Wellington': {'FISH': 4.0, 'CHIPS': 1.0}, 'Auckland': {'FISH': 5.0, 'CHIPS': 3.0}}>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Parameters indexed over two sets can also be specified in tabular format:
|
||||||
|
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
|
||||||
|
>>> data = Amply("""
|
||||||
|
... param COSTS{CITIES, PRODUCTS};
|
||||||
|
... param COSTS: FISH CHIPS :=
|
||||||
|
... Auckland 5 3
|
||||||
|
... Wellington 4 1
|
||||||
|
... ;
|
||||||
|
... """)
|
||||||
|
>>> print data.COSTS
|
||||||
|
<ParamObject: {'Wellington': {'FISH': 4.0, 'CHIPS': 1.0}, 'Auckland': {'FISH': 5.0, 'CHIPS': 3.0}}>
|
||||||
|
|
||||||
|
Tabular data can also be transposed:
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
|
||||||
|
>>> data = Amply("""
|
||||||
|
... param COSTS{CITIES, PRODUCTS};
|
||||||
|
... param COSTS (tr): Auckland Wellington :=
|
||||||
|
... FISH 5 4
|
||||||
|
... CHIPS 3 1
|
||||||
|
... ;
|
||||||
|
... """)
|
||||||
|
>>> print data.COSTS
|
||||||
|
<ParamObject: {'Wellington': {'FISH': 4.0, 'CHIPS': 1.0}, 'Auckland': {'FISH': 5.0, 'CHIPS': 3.0}}>
|
||||||
|
|
||||||
|
|
||||||
|
Slices can be combined with tabular data for parameters indexed over more than
|
||||||
|
2 sets:
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
|
||||||
|
>>> data = Amply("""
|
||||||
|
... param COSTS{CITIES, PRODUCTS, SIZE};
|
||||||
|
... param COSTS :=
|
||||||
|
... [Auckland, *, *] : SMALL LARGE :=
|
||||||
|
... FISH 5 9
|
||||||
|
... CHIPS 3 5
|
||||||
|
... [Wellington, *, *] : SMALL LARGE :=
|
||||||
|
... FISH 4 7
|
||||||
|
... CHIPS 1 2
|
||||||
|
... ;
|
||||||
|
... """)
|
||||||
|
>>> print data.COSTS
|
||||||
|
<ParamObject: {'Wellington': {'FISH': {'SMALL': 4.0, 'LARGE': 7.0}, 'CHIPS': {'SMALL': 1.0, 'LARGE': 2.0}}, 'Auckland': {'FISH': {'SMALL': 5.0, 'LARGE': 9.0}, '
|
||||||
|
|
||||||
|
|
||||||
|
API
|
||||||
|
---
|
||||||
|
|
||||||
|
All functionality is contained within the ``Amply`` class.
|
||||||
|
|
||||||
|
.. class:: Amply(string="")
|
||||||
|
|
||||||
|
.. method:: load_string(string)
|
||||||
|
|
||||||
|
Parse string data.
|
||||||
|
|
||||||
|
.. method:: load_file(file)
|
||||||
|
|
||||||
|
Parse contents of file or file-like object (has a read() method).
|
||||||
|
|
||||||
|
.. staticmethod:: from_file(file)
|
||||||
|
|
||||||
|
Alternate constructor. Create Amply object from contents of file or file-like object.
|
||||||
|
|
||||||
|
|
||||||
|
The parsed data structures can then be accessed from an ``Amply`` object via
|
||||||
|
attribute lookup (if the name of the symbol is a valid Python name) or item
|
||||||
|
lookup. ::
|
||||||
|
|
||||||
|
from pulp import Amply
|
||||||
|
|
||||||
|
data = Amply("set CITIES := Auckland Hamilton Wellington")
|
||||||
|
|
||||||
|
# attribute lookup
|
||||||
|
assert data.CITIES == ['Auckland', 'Hamilton', 'Wellington']
|
||||||
|
|
||||||
|
# item lookup
|
||||||
|
assert data['CITIES'] == data.CITIES
|
||||||
|
|
||||||
|
Note that additional data may be loaded into an Amply object simply by calling
|
||||||
|
one of its methods. A common idiom might be to specify the set and parameter
|
||||||
|
declarations within your Python script, then load the actual data from
|
||||||
|
external files. ::
|
||||||
|
|
||||||
|
from pulp import Amply
|
||||||
|
|
||||||
|
data = Amply("""
|
||||||
|
set CITIES;
|
||||||
|
set ROUTES dimen 2;
|
||||||
|
param COSTS{ROUTES};
|
||||||
|
param DISTANCES{ROUTES};
|
||||||
|
""")
|
||||||
|
|
||||||
|
for data_file in ('cities.dat', 'routes.dat', 'costs.dat', 'distances.dat'):
|
||||||
|
data.load_file(open(data_file))
|
||||||
|
|
||||||
|
.. Commented out the below, not sure if we need it (incomplete)
|
||||||
|
|
||||||
|
Reference
|
||||||
|
---------
|
||||||
|
|
||||||
|
Sets
|
||||||
|
^^^^
|
||||||
|
|
||||||
|
Set declarations
|
||||||
|
~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
A set declaration is an extremely limited version of set statements which are valid in AMPL models.
|
||||||
|
They determine the *subscript domain* and *data dimension* of the set. If not specified, the default
|
||||||
|
subscript domain is an empty set and the default dimension is 1.
|
||||||
|
|
||||||
|
.. productionlist::
|
||||||
|
set_def_stmt: "set" `name` [`subscript_domain`] ["dimen" `integer`] ";"
|
||||||
|
subscript_domain: "{" `name` ("," `name`)* "}"
|
||||||
|
|
||||||
|
The following statment declares a set named "countries". ::
|
||||||
|
|
||||||
|
set countries;
|
||||||
|
|
||||||
|
The following statement declares a set named "cities" which is indexed over "countries". ::
|
||||||
|
|
||||||
|
set cities {countries};
|
||||||
|
|
||||||
|
The following declares a set named "routes" with 2d data. ::
|
||||||
|
|
||||||
|
set routes dimen 2;
|
||||||
|
|
||||||
|
Set data statements
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
A set data statement is used to specify the members of a set. It consists of one or more
|
||||||
|
*data records*. There are four types of data records: simple data, slice records, matrix
|
||||||
|
data and transposed matrix data.
|
||||||
|
|
||||||
|
.. productionlist::
|
||||||
|
set_stmt: "set" `name` [`set_member`] `data_record`+ ";"
|
||||||
|
data_record: `simple_data` | `set_slice_record` | `matrix_data` | `tr_matrix_data`
|
||||||
|
|
||||||
|
Simple Data
|
||||||
|
############
|
||||||
|
|
||||||
|
A simple data record is an optionally comma-separated list of data values.
|
||||||
|
|
||||||
|
.. productionlist::
|
||||||
|
simple_data: `data` ([","] `data`)*
|
||||||
|
|
||||||
|
For instance: ::
|
||||||
|
|
||||||
|
set CITIES := Auckland Hamilton 'Palmerston North' Wellington;
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
set ROUTES dimen 2;
|
||||||
|
set ROUTES := (Auckland, Hamilton) (Auckland, Wellington);
|
||||||
|
|
||||||
|
Slice Records
|
||||||
|
###############
|
||||||
|
|
||||||
|
Slice records are used to simplify the entry of multi-dimensional sets. They allow you to partially
|
||||||
|
specify the values of elements. A slice affects all data records that follow it (until a new slice
|
||||||
|
is specified).
|
||||||
|
|
||||||
|
.. productionlist::
|
||||||
|
set_slice_record: "(" `set_slice_component` ("," `set_slice_component`)* ")"
|
||||||
|
set_slice_component: `number` | `symbol` | "*"
|
||||||
|
|
||||||
|
This is best demonstrated by some examples. The sets A and B are identical: ::
|
||||||
|
|
||||||
|
set A dimen 3;
|
||||||
|
set B dimen 3;
|
||||||
|
|
||||||
|
set A := (1, 2, 3) (1, 3, 2) (1, 4, 6) (1, 8, 8) (2, 1, 3) (2, 1, 1) (2, 1, 2);
|
||||||
|
set B := (1, *, *) (2, 3) (3, 2) (4, 6) (8, 8) (2, 1, *) 3 1 2;
|
||||||
|
|
||||||
|
The number of asterisks in a slice is called the *slice dimension*. Any data records that follow
|
||||||
|
are interpreted as being of the same dimension; the value is taken as the value of the slice with
|
||||||
|
the asterisks replaced with the value of the record.
|
||||||
|
|
||||||
|
Matrix records
|
||||||
|
################
|
||||||
|
|
||||||
|
Matrix records are a convenient way of specifying 2-dimensional data. The data record looks like
|
||||||
|
a matrix with row and column headings, where the values are either '+' if the combination is in
|
||||||
|
the set, and '-' if the combination is not in the set. A common use-case is for defining the
|
||||||
|
set of arcs that exist between a set of nodes.
|
||||||
|
|
||||||
|
.. productionlist::
|
||||||
|
matrix_data: ":" `matrix_columns` ":=" `matrix_row`+
|
||||||
|
matrix_columns: `data`+
|
||||||
|
matrix_row: `data` ("+"|"-")+
|
||||||
|
tr_matrix_data: "(tr)" `matrix_data`
|
||||||
|
|
||||||
|
Matrices can also be transposed by including ``(tr)`` immediately preceding the record.
|
||||||
|
|
||||||
|
In the example below the sets A, B and C are identical: ::
|
||||||
|
|
||||||
|
set A dimen 2;
|
||||||
|
set B dimen 2;
|
||||||
|
set C dimen 2;
|
||||||
|
|
||||||
|
set A := (1, 1) (1, 3) (2, 2) (3, 1) (3, 2) (3, 3);
|
||||||
|
set B : 1 2 3 :=
|
||||||
|
1 + - +
|
||||||
|
2 - + -
|
||||||
|
3 + + +
|
||||||
|
;
|
||||||
|
set C (tr) : 1 2 3 :=
|
||||||
|
1 + - +
|
||||||
|
2 - + +
|
||||||
|
3 + - +
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
Matrices can be used for sets with higher dimensions by placing them after 2 dimensional
|
||||||
|
slice records.
|
||||||
|
|
||||||
|
|
||||||
|
Set examples
|
||||||
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Plain Data
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Tabular data
|
||||||
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Tabbing Data
|
||||||
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
392
doc/source/main/basic_python_coding.rst
Normal file
@@ -0,0 +1,392 @@
|
|||||||
|
Basic Python Coding
|
||||||
|
===================
|
||||||
|
|
||||||
|
In this course you will learn basic programming in Python, but there is also
|
||||||
|
excellent Python Language reference material available on the internet freely.
|
||||||
|
You can download the book `Dive Into Python <http://www.diveintopython.org/>`_
|
||||||
|
or there are a host of `Beginners Guides <http://wiki.python.org/moin/BeginnersGuide>`_
|
||||||
|
to Python on the Python Website. Follow the links to either:
|
||||||
|
|
||||||
|
+ `BeginnersGuide/NonProgrammers <http://wiki.python.org/moin/BeginnersGuide/NonProgrammers>`_
|
||||||
|
+ `BeginnersGuide/Programmers <http://wiki.python.org/moin/BeginnersGuide/Programmers>`_
|
||||||
|
|
||||||
|
depending on your level of current programming knowledge. The code sections
|
||||||
|
below assume a knowledge of fundamental programming principles and mainly
|
||||||
|
focus on Syntax specific to programming in Python.
|
||||||
|
|
||||||
|
Note: >>> represents a Python command line prompt.
|
||||||
|
|
||||||
|
|
||||||
|
Loops in Python
|
||||||
|
---------------
|
||||||
|
|
||||||
|
for Loop
|
||||||
|
~~~~~~~~
|
||||||
|
|
||||||
|
The general format is:
|
||||||
|
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
for variable in sequence:
|
||||||
|
#some commands
|
||||||
|
#other commands after for loop
|
||||||
|
|
||||||
|
|
||||||
|
Note that the formatting (indents and new lines) governs the end of the for
|
||||||
|
loop, whereas the start of the loop is the colon :.
|
||||||
|
|
||||||
|
Observe the loop below, which is similar to loops you will be using in the
|
||||||
|
course. The variable i moves through the string list becoming each string in
|
||||||
|
turn. The top section is the code in a .py file and the bottom section shows
|
||||||
|
the output
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
#The following code demonstrates a list with strings
|
||||||
|
ingredientslist = ["Rice","Water","Jelly"]
|
||||||
|
for i in ingredientslist:
|
||||||
|
print i
|
||||||
|
print "No longer in the loop"
|
||||||
|
|
||||||
|
gives
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
Rice
|
||||||
|
Water
|
||||||
|
Jelly
|
||||||
|
No longer in the loop
|
||||||
|
|
||||||
|
|
||||||
|
while Loop
|
||||||
|
~~~~~~~~~~
|
||||||
|
|
||||||
|
These are similar to for loops except they continue to loop until the specified
|
||||||
|
condition is no longer true. A while loop is not told to work through any
|
||||||
|
specific sequence.
|
||||||
|
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
i = 3
|
||||||
|
while i <= 15:
|
||||||
|
# some commands
|
||||||
|
i = i + 1 # a command that will eventually end the loop is naturally
|
||||||
|
required
|
||||||
|
# other commands after while loop
|
||||||
|
|
||||||
|
|
||||||
|
For this specific simple while loop, it would have been better to do as a for
|
||||||
|
loop but it demonstrates the syntax. while loops are useful if the number of
|
||||||
|
iterations before the loop needs to end, is unknown.
|
||||||
|
|
||||||
|
The if statement
|
||||||
|
~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
This is performed in much the same way as the loops above. The key identifier is
|
||||||
|
the colon : to start the statements and the end of indentation to end it.
|
||||||
|
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
if j in testlist:
|
||||||
|
# some commands
|
||||||
|
elif j == 5:
|
||||||
|
# some commands
|
||||||
|
else:
|
||||||
|
# some commands
|
||||||
|
|
||||||
|
|
||||||
|
Here it is shown that "elif" (else if) and "else" can also be used after an if
|
||||||
|
statement. "else" can in fact be used after both the loops in the same way.
|
||||||
|
|
||||||
|
Array types in python
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
Lists
|
||||||
|
~~~~~
|
||||||
|
|
||||||
|
A list is simply a sequence of variables grouped together. The range function is
|
||||||
|
often used to create lists of integers, with the general format of
|
||||||
|
range(start,stop,step). The default for start is 0 and the default for step is
|
||||||
|
1.
|
||||||
|
|
||||||
|
|
||||||
|
>>> range(3,8)
|
||||||
|
[3,4,5,6,7]
|
||||||
|
|
||||||
|
|
||||||
|
This is a list/sequence. As well as integers, lists can also have strings in
|
||||||
|
them or a mix of integers, floats and strings. They can be created by a loop
|
||||||
|
(as shown in the next section) or by explicit creation (below). Note that the
|
||||||
|
print statement will display a string/variable/list/... to the user
|
||||||
|
|
||||||
|
|
||||||
|
>>> a = [5,8,"pt"]
|
||||||
|
>>> print a
|
||||||
|
[5,8,'pt']
|
||||||
|
>>> print a[0]
|
||||||
|
5
|
||||||
|
|
||||||
|
Tuples
|
||||||
|
~~~~~~
|
||||||
|
|
||||||
|
Tuples are basically the same as lists, but with the important difference that
|
||||||
|
they cannot be modified once they have been created. They are assigned by:
|
||||||
|
|
||||||
|
|
||||||
|
>>> x = (4,1,8,"string",[1,0],("j",4,"o"),14)
|
||||||
|
|
||||||
|
|
||||||
|
Tuples can have any type of number, strings, lists, other tuples, functions and
|
||||||
|
objects, inside them. Also note that the first element in the tuple is numbered
|
||||||
|
as element "zero". Accessing this data is done by:
|
||||||
|
|
||||||
|
|
||||||
|
>>> x[0]
|
||||||
|
4
|
||||||
|
>>> x[3]
|
||||||
|
"string"
|
||||||
|
|
||||||
|
|
||||||
|
Dictionaries
|
||||||
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
|
A Dictionary is a list of reference keys each with associated data, whereby the
|
||||||
|
order does not affect the operation of the dictionary at all. With dictionaries,
|
||||||
|
the keys are not consecutive integers (unlike lists), and instead could be
|
||||||
|
integers, floats or strings. This will become clear:
|
||||||
|
|
||||||
|
|
||||||
|
>>> x = {} # creates a new empty dictionary - note the curly brackets denoting the creation of a dictionary
|
||||||
|
>>> x[4] = "programming" # the string "programming" is added to the dictionary x, with "4" as it's reference
|
||||||
|
>>> x["games"] = 12
|
||||||
|
>>> print x["games"]
|
||||||
|
12
|
||||||
|
|
||||||
|
|
||||||
|
In a dictionary, the reference keys and the stored values can be any type of
|
||||||
|
input. New dictionary elements are added as they are created (with a list, you
|
||||||
|
cannot access or write to a place in the list that exceeds the initially defined
|
||||||
|
list dimensions).
|
||||||
|
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
costs = {"CHICKEN": 1.3, "BEEF": 0.8, "MUTTON": 12}
|
||||||
|
print "Cost of Meats"
|
||||||
|
for i in costs:
|
||||||
|
print i
|
||||||
|
print costs[i]
|
||||||
|
costs["LAMB"] = 5
|
||||||
|
print "Updated Costs of Meats"
|
||||||
|
for i in costs:
|
||||||
|
print i
|
||||||
|
print costs[i]
|
||||||
|
|
||||||
|
gives
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
Cost of Meats
|
||||||
|
CHICKEN
|
||||||
|
1.3
|
||||||
|
MUTTON
|
||||||
|
12
|
||||||
|
BEEF
|
||||||
|
0.8
|
||||||
|
Updated Costs of Meats
|
||||||
|
LAMB
|
||||||
|
5
|
||||||
|
CHICKEN
|
||||||
|
1.3
|
||||||
|
MUTTON
|
||||||
|
12
|
||||||
|
BEEF
|
||||||
|
0.8
|
||||||
|
|
||||||
|
|
||||||
|
In the above example, the dictionary is created using curly brackets and colons
|
||||||
|
to represent the assignment of data to the dictionary keys. The variable i is
|
||||||
|
assigned to each of the keys in turn (in the same way it would be for a list
|
||||||
|
with
|
||||||
|
|
||||||
|
>>> for i in range(1,10)
|
||||||
|
|
||||||
|
). Then the dictionary is called with this key, and
|
||||||
|
it returns the data stored under that key name. These types of for loops using
|
||||||
|
dictionaries will be highly relevant in using PuLP to model LPs in this course.
|
||||||
|
|
||||||
|
List/Tuple/Dictionary Syntax Note
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Note that the creation of a:
|
||||||
|
|
||||||
|
- list is done with square brackets [];
|
||||||
|
- tuple is done with round brackets and a comma (,);
|
||||||
|
- dictionary is done with parentheses{}.
|
||||||
|
|
||||||
|
After creation however, when accessing elements in the list/tuple/dictionary,
|
||||||
|
the operation is always performed with square brackets (i.e a[3]?). If a was a
|
||||||
|
list or tuple, this would return the fourth element. If a was a dictionary it
|
||||||
|
would return the data stored with a reference key of 3.
|
||||||
|
|
||||||
|
List Comprehensions
|
||||||
|
~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Python supports List Comprehensions which are a fast and concise way to create
|
||||||
|
lists without using multiple lines. They are easily understandable when simple,
|
||||||
|
and you will be using them in your code for this course.
|
||||||
|
|
||||||
|
|
||||||
|
>>> a = [i for i in range(5)]
|
||||||
|
>>> a
|
||||||
|
[0, 1, 2, 3, 4]
|
||||||
|
|
||||||
|
|
||||||
|
This statement above will create the list [0,1,2,3,4] and assign it to the
|
||||||
|
variable "a".
|
||||||
|
|
||||||
|
|
||||||
|
>>> odds = [i for i in range(25) if i%2==1]
|
||||||
|
>>> odds
|
||||||
|
[1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23]
|
||||||
|
|
||||||
|
This statement above uses the if statement and the modulus operator(%) so that
|
||||||
|
only odd numbers are included in the list: [1,3,5,...,19,21,23]. (Note: The
|
||||||
|
modulus operator calculates the remainder from an integer division.)
|
||||||
|
|
||||||
|
|
||||||
|
>>> fifths = [i for i in range(25) if i%5==0]
|
||||||
|
>>> fifths
|
||||||
|
[0, 5, 10, 15, 20]
|
||||||
|
|
||||||
|
This will create a list with every fifth value in it [0,5,10,15,20]. Existing
|
||||||
|
lists can also be used in the creation of new lists below:
|
||||||
|
|
||||||
|
>>> a = [i for i in range(25) if (i in odds and i not in fifths)]
|
||||||
|
|
||||||
|
|
||||||
|
Note that this could also have been done in one step from scratch:
|
||||||
|
|
||||||
|
>>> a = [i for i in range(25) if (i%2==1 and i%5==0)]
|
||||||
|
|
||||||
|
For a challenge you can try creating
|
||||||
|
|
||||||
|
a. a list of prime numbers up to 100, and
|
||||||
|
#. a list of all "perfect" numbers.
|
||||||
|
|
||||||
|
|
||||||
|
`More List Comprehensions Examples <http://www.secnetix.de/olli/Python/list_comprehensions.hawk>`_
|
||||||
|
|
||||||
|
`Wikipedia: Perfect Numbers <http://en.wikipedia.org/wiki/Perfect_numbers>`_.
|
||||||
|
|
||||||
|
Other important language features
|
||||||
|
---------------------------------
|
||||||
|
|
||||||
|
Commenting in Python
|
||||||
|
~~~~~~~~~~~~~~~~~~~~
|
||||||
|
Commenting at the top of a file is done using """ to start and to end the
|
||||||
|
comment section. Commenting done throughout the code is done using the hash
|
||||||
|
# symbol at the start of the line.
|
||||||
|
|
||||||
|
Import Statement
|
||||||
|
~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
At the top of all your Python coding that you intend to use PuLP to model, you
|
||||||
|
will need the import statement. This statement makes the contents of another
|
||||||
|
module (file of program code) available in the module you are currently writing
|
||||||
|
i.e. functions and values defined in pulp.py that you will be required to call,
|
||||||
|
become usable. In this course you will use:
|
||||||
|
|
||||||
|
>>> from pulp import *
|
||||||
|
|
||||||
|
The asterisk represents that you are importing all names from the module of
|
||||||
|
pulp. Calling a function defined in pulp.py now can be done as if they were
|
||||||
|
defined in your own module.
|
||||||
|
|
||||||
|
Functions
|
||||||
|
~~~~~~~~~
|
||||||
|
|
||||||
|
Functions in Python are defined by: (def is short for define)
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
def name(inputparameter1, inputparameter2, . . .):
|
||||||
|
#function body
|
||||||
|
|
||||||
|
For a real example, note that if inputs are assigned a value in the function
|
||||||
|
definition, that is the default value, and will be used only if no other value
|
||||||
|
is passed in. The order of the input parameters (in the definition) does not
|
||||||
|
matter at all, as long as when the function is called, the positional parameters
|
||||||
|
are entered in the corresponding order. If keywords are used the order of the
|
||||||
|
parameters does not matter at all:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
def string_appender(head='begin', tail='end', end_message='EOL'):
|
||||||
|
result = head + tail + end_message
|
||||||
|
return result
|
||||||
|
|
||||||
|
>>> string_appender('newbegin', end_message = 'StringOver')
|
||||||
|
newbeginendStringOver
|
||||||
|
|
||||||
|
In the above example, the output from the function call is printed. The default
|
||||||
|
value for head is 'begin', but instead the input of 'newbegin' was used. The
|
||||||
|
default value for tail of 'end' was used. And the input value of endmessage was
|
||||||
|
used. Note that end_message must be specified as a key word argument as no
|
||||||
|
value is given for tail
|
||||||
|
|
||||||
|
Classes
|
||||||
|
~~~~~~~
|
||||||
|
|
||||||
|
To demonstrate how classes work in Python, look at the following class structure.
|
||||||
|
|
||||||
|
The class name is Pattern, and it contains several class variables which are
|
||||||
|
relevant to any instance of the Pattern class (i.e a Pattern). The functions are
|
||||||
|
|
||||||
|
__init__
|
||||||
|
function which creates an instance of the Pattern
|
||||||
|
class and assigns the attributes of name and lengthsdict using self.
|
||||||
|
|
||||||
|
__str__
|
||||||
|
function defines what to return if the class instance is printed.
|
||||||
|
|
||||||
|
trim
|
||||||
|
function acts like any normal function, except as with all class functions,
|
||||||
|
self must be in the input brackets.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
class Pattern:
|
||||||
|
"""
|
||||||
|
Information on a specific pattern in the SpongeRoll Problem
|
||||||
|
"""
|
||||||
|
cost = 1
|
||||||
|
trimValue = 0.04
|
||||||
|
totalRollLength = 20
|
||||||
|
lenOpts = [5, 7, 9]
|
||||||
|
|
||||||
|
def __init__(self,name,lengths = None):
|
||||||
|
self.name = name
|
||||||
|
self.lengthsdict = dict(zip(self.lenOpts,lengths))
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
def trim(self):
|
||||||
|
return Pattern.totalRollLength - sum([int(i)*self.lengthsdict[i] for i in self.lengthsdict])
|
||||||
|
|
||||||
|
This class could be used as follows:
|
||||||
|
|
||||||
|
>>> Pattern.cost # The class attributes can be accessed without making an instance of the class
|
||||||
|
1
|
||||||
|
>>> a = Pattern("PatternA",[1,0,1])
|
||||||
|
>>> a.cost # a is now an instance of the Pattern class and is associated with Pattern class variables
|
||||||
|
1
|
||||||
|
>>> print a # This calls the Pattern.__str__() function
|
||||||
|
"PatternA"
|
||||||
|
>>> a.trim() # This calls the Pattern.trim() function. Note that no input is required.
|
||||||
|
|
||||||
|
The self in the function definition is an implied input
|
||||||
BIN
doc/source/main/images/bandb.jpg
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
doc/source/main/images/modeling_process.jpg
Normal file
|
After Width: | Height: | Size: 64 KiB |
BIN
doc/source/main/images/or_methodology.jpg
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
doc/source/main/images/sensitivity_excel.jpg
Normal file
|
After Width: | Height: | Size: 138 KiB |
16
doc/source/main/index.rst
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
|
||||||
|
Main Topics
|
||||||
|
=====================
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
|
||||||
|
the_optimisation_process
|
||||||
|
optimisation_concepts
|
||||||
|
basic_python_coding
|
||||||
|
installing_pulp_at_home
|
||||||
|
amply
|
||||||
|
|
||||||
|
|
||||||
13
doc/source/main/installing_pulp_at_home.rst
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
.. _installation:
|
||||||
|
|
||||||
|
Installing PuLP at Home
|
||||||
|
=======================
|
||||||
|
|
||||||
|
PuLP is a free open source software written in Python. It is used to describe
|
||||||
|
optimisation problems as mathematical models. PuLP can then call any of numerous
|
||||||
|
external LP solvers (CBC, GLPK, CPLEX, Gurobi etc) to solve this model and then
|
||||||
|
use python commands to manipulate and display the solution.
|
||||||
|
|
||||||
|
.. include:: ../../../INSTALL
|
||||||
|
|
||||||
|
|
||||||
42
doc/source/main/optimisation_concepts.rst
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
Optimisation Concepts
|
||||||
|
=====================
|
||||||
|
|
||||||
|
Linear Programing
|
||||||
|
-----------------
|
||||||
|
The simplest type of mathematical program is a linear program. For your
|
||||||
|
mathematical program to be a linear program you need the following
|
||||||
|
conditions to be true:
|
||||||
|
|
||||||
|
* The decision variables must be real variables;
|
||||||
|
* The objective must be a linear expression;
|
||||||
|
* The constraints must be linear expressions.
|
||||||
|
|
||||||
|
Linear expressions are any expression of the form
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
a_1 x_1 + a_2 x_2 + a_3 x_3 + ... a_n x_n \{<= , =, >=\} b
|
||||||
|
|
||||||
|
where the :math:`a_i` and :math:`b` are known constants and :math:`x_i` are variables. The process
|
||||||
|
of solving a linear program is called linear programing. Linear programing
|
||||||
|
is done via the Revised Simplex Method (also known as the Primal Simplex Method),
|
||||||
|
the Dual Simplex Method or an Interior Point Method. Some solvers like cplex
|
||||||
|
allow you to specify which method you use, but we won’t go into further detail
|
||||||
|
here.
|
||||||
|
|
||||||
|
Integer Programing
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Integer programs are almost identical to linear programs with one very
|
||||||
|
important exception. Some of the decision variables in integer programs may
|
||||||
|
need to have only integer values. The variables are known as integer variables.
|
||||||
|
Since most integer programs contain a mix of continuous variables and integer
|
||||||
|
variables they are often known as mixed integer programs. While the change
|
||||||
|
from linear programing is a minor one, the effect on the solution process is
|
||||||
|
enormous. Integer programs can be very difficult problems to solve and there
|
||||||
|
is a lot of current research finding “good” ways to solve integer programs.
|
||||||
|
Integer programs can be solved using the branch-and-bound process.
|
||||||
|
|
||||||
|
Note For MIPs of any reasonable size the solution time grows
|
||||||
|
exponentially as the number of integer variables increases.
|
||||||
|
|
||||||
113
doc/source/main/the_optimisation_process.rst
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
The Optimisation Process
|
||||||
|
========================
|
||||||
|
|
||||||
|
Solving an optimisation problem is not a linear process, but the process can be
|
||||||
|
broken down into five general steps:
|
||||||
|
|
||||||
|
+ Getting the problem description
|
||||||
|
+ Formulating the mathematical program
|
||||||
|
+ Solving the mathematical program
|
||||||
|
+ Performing some post-optimal analysis
|
||||||
|
+ Presenting the solution and analysis
|
||||||
|
|
||||||
|
However, there are often "feedback loops" within this process. For example,
|
||||||
|
after formulating and solving an optimisation problem, you will often want to
|
||||||
|
consider the validity of your solution (often consulting with the person who
|
||||||
|
provided the problem description). If your solution is invalid you may need to
|
||||||
|
alter or update your formulation to incorporate your new understanding of the
|
||||||
|
actual problem. This process is shown in the Operations Research Methodology
|
||||||
|
Diagram.
|
||||||
|
|
||||||
|
.. image:: images/or_methodology.jpg
|
||||||
|
|
||||||
|
The modeling process starts with a well-defined model description, then uses
|
||||||
|
mathematics to formulate a mathematical program. Next, the modeler enters the
|
||||||
|
mathematical program into some solver software, e.g., Excel and solves the
|
||||||
|
model. Finally, the solution is translated into a decision in terms of the
|
||||||
|
original model description.
|
||||||
|
|
||||||
|
Using Python gives you a "shortcut" through the modeling process. By formulating
|
||||||
|
the mathematical program in Python you have already put it into a form that
|
||||||
|
can be used easily by PuLP the modeller to call many solvers, e.g. CPLEX, COIN,
|
||||||
|
gurobi so you don't need to enter the mathematical program into the solver
|
||||||
|
software. However, you usually don't put any "hard" numbers into your
|
||||||
|
formulation, instead you "populate" your model using data files, so there is
|
||||||
|
some work involved in creating the appropriate data file. The advantage of using
|
||||||
|
data files is that the same model may used many times with different data sets.
|
||||||
|
|
||||||
|
The Modeling Process
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
The modeling process is a "neat and tidy" simplification of the optimisation
|
||||||
|
process. Let's consider the five steps of the optimisation process in more
|
||||||
|
detail:
|
||||||
|
|
||||||
|
Getting the Problem Description
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The aim of this step is to come up with a formal, rigourous model description.
|
||||||
|
Usually you start an optimisation project with an abstract description of a
|
||||||
|
problem and some data. Often you need to spend some time talking with the
|
||||||
|
person providing the problem (usually known as the client). By talking with the
|
||||||
|
client and considering the data available you can come up with the more
|
||||||
|
rigourous model description you are used to. Sometimes not all the data will be
|
||||||
|
relevant or you will need to ask the client if they can provide some other
|
||||||
|
data. Sometimes the limitations of the available data may change your model
|
||||||
|
description and subsequent formulation significantly.
|
||||||
|
|
||||||
|
Formulating the mathematical program
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
In this step we identify the key quantifiable decisions, restrictions and goals
|
||||||
|
from the problem description, and capture their interdependencies in a
|
||||||
|
mathematical model. We can break the formulation process into 4 key steps:
|
||||||
|
|
||||||
|
* Identify the Decision Variables paying particular attention to units (for example: we need to decide how many hours per week each process will run for).
|
||||||
|
* Formulate the Objective Function using the decision variables, we can construct a minimise or maximise objective function. The objective function typically reflects the total cost, or total profit, for a given value of the decision variables.
|
||||||
|
* Formulate the Constraints, either logical (for example, we cannot work for a negative number of hours), or explicit to the problem description. Again, the constraints are expressed in terms of the decision variables.
|
||||||
|
* Identify the Data needed for the objective function and constraints. To solve your mathematical program you will need to have some "hard numbers" as variable bounds and/or variable coefficients in your objective function and/or constraints.
|
||||||
|
|
||||||
|
Solving the mathematical program
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
For relatively simple or well understood problems the mathematical model can
|
||||||
|
often be solved to optimality (i.e., the best possible solution is identified).
|
||||||
|
This is done using algorithms such as the Revised Simplex Method
|
||||||
|
or Interior Point Methods. However, many
|
||||||
|
industrial problems would take too long to solve to optimality using these
|
||||||
|
techniques, and so are solved using heuristic methods which do not guarantee optimality.
|
||||||
|
|
||||||
|
Performing some post-optimal analysis
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
Often there is uncertainty in the problem description (either with the accuracy
|
||||||
|
of the data provided, or with the value(s) of data in the future). In this
|
||||||
|
situation the robustness of our solution can be examined by performing
|
||||||
|
post-optimal analysis. This involves identifying how the optimal solution would
|
||||||
|
change under various changes to the formulation (for example, what would be the
|
||||||
|
effect of a given cost increasing, or a particular machine failing?). This sort
|
||||||
|
of analysis can also be useful for making tactical or strategic decisions (for
|
||||||
|
example, if we invested in opening another factory, what effect would this have on our revenue?).
|
||||||
|
|
||||||
|
Another important consideration in this step (and the next) is the validation of
|
||||||
|
the mathematical program's solution. You should carefully consider what the
|
||||||
|
solution's variable values mean in terms of the original problem description.
|
||||||
|
Make sure they make sense to you and, more importantly, your client (which is
|
||||||
|
why the next step, presenting the solution and analysis is important).
|
||||||
|
|
||||||
|
Presenting the solution and analysis
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
A crucial step in the optimisation process is the presentation of the solution
|
||||||
|
and any post-optimal analysis. The translation from a mathematical program's
|
||||||
|
solution back into a concise and comprehensible summary is as important as the
|
||||||
|
translation from the problem description into the mathematical program. Key
|
||||||
|
observations and decisions generated via optimisation must be presented in an
|
||||||
|
easily understandable way for the client or project stakeholders.
|
||||||
|
|
||||||
|
Your presentation is a crucial first step in the implementation of the decisions
|
||||||
|
generated by your mathematical program. If the decisions and their consequences
|
||||||
|
(often determined by the mathematical program constraints) are not presented
|
||||||
|
clearly and intelligently your optimal decision will never be used.
|
||||||
|
|
||||||
|
This step is also your chance to suggest other work in the future. This could include:
|
||||||
|
|
||||||
|
* Periodic monitoring of the validity of your mathematical program;
|
||||||
|
* Further analysis of your solution, looking for other benefits for your client;
|
||||||
|
* Identification of future optimisation opportunities.
|
||||||
107
doc/source/pulp.rst
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
=====================================
|
||||||
|
:mod:`pulp`: Pulp classes
|
||||||
|
=====================================
|
||||||
|
.. module:: pulp
|
||||||
|
|
||||||
|
.. currentmodule:: pulp
|
||||||
|
|
||||||
|
.. autosummary::
|
||||||
|
|
||||||
|
LpProblem
|
||||||
|
LpVariable
|
||||||
|
LpAffineExpression
|
||||||
|
LpConstraint
|
||||||
|
LpConstraint.makeElasticSubProblem
|
||||||
|
FixedElasticSubProblem
|
||||||
|
|
||||||
|
.. todo:: LpFractionConstraint, FractionElasticSubProblem
|
||||||
|
|
||||||
|
The LpProblem Class
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
.. autoclass:: LpProblem
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
Three important attributes of the problem are:
|
||||||
|
|
||||||
|
.. attribute:: objective
|
||||||
|
|
||||||
|
The objective of the problem, an :obj:`LpAffineExpression`
|
||||||
|
|
||||||
|
.. attribute:: constraints
|
||||||
|
|
||||||
|
An :class:`ordered dictionary<odict.OrderedDict>` of
|
||||||
|
:class:`constraints<LpConstraint>` of the problem - indexed by their names.
|
||||||
|
|
||||||
|
.. attribute:: status
|
||||||
|
|
||||||
|
The return :data:`status <pulp.constants.LpStatus>`
|
||||||
|
of the problem from the solver.
|
||||||
|
|
||||||
|
Some of the more important methods:
|
||||||
|
|
||||||
|
.. automethod:: solve
|
||||||
|
.. automethod:: roundSolution
|
||||||
|
.. automethod:: setObjective
|
||||||
|
.. automethod:: writeLP
|
||||||
|
|
||||||
|
Variables and Expressions
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
.. autoclass:: LpElement
|
||||||
|
:members:
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
.. autoclass:: LpVariable
|
||||||
|
:members:
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
>>> x = LpVariable('x',lowBound = 0, cat='Continuous')
|
||||||
|
>>> y = LpVariable('y', upBound = 5, cat='Integer')
|
||||||
|
|
||||||
|
gives :math:`x \in [0,\infty)`, :math:`y \in (-\infty, 5]`, an
|
||||||
|
integer.
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
.. autoclass:: LpAffineExpression
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
In brief, :math:`\textsf{LpAffineExpression([(x[i],a[i]) for i in
|
||||||
|
I])} = \sum_{i \in I} a_i x_i` where (note the order):
|
||||||
|
|
||||||
|
* ``x[i]`` is an :class:`LpVariable`
|
||||||
|
* ``a[i]`` is a numerical coefficient.
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
.. autofunction:: lpSum
|
||||||
|
|
||||||
|
|
||||||
|
Constraints
|
||||||
|
-----------
|
||||||
|
.. autoclass:: LpConstraint
|
||||||
|
:show-inheritance:
|
||||||
|
:members: makeElasticSubProblem
|
||||||
|
|
||||||
|
.. include:: documentation/elastic.rst
|
||||||
|
|
||||||
|
.. autoclass:: FixedElasticSubProblem
|
||||||
|
:show-inheritance:
|
||||||
|
:members:
|
||||||
|
|
||||||
|
Combinations and Permutations
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
.. autofunction:: combination
|
||||||
|
|
||||||
|
.. autofunction:: allcombinations
|
||||||
|
|
||||||
|
.. autofunction:: permutation
|
||||||
|
|
||||||
|
.. autofunction:: allpermutations
|
||||||
|
|
||||||
|
.. autofunction:: value
|
||||||
|
|
||||||
11
doc/source/solvers.rst
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
:mod:`pulp.solvers` Interface to Solvers
|
||||||
|
========================================
|
||||||
|
|
||||||
|
|
||||||
|
.. automodule:: pulp.solvers
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
|
||||||
11
docs.cfg
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
[buildout]
|
||||||
|
extends = buildout.cfg
|
||||||
|
parts += sphinx
|
||||||
|
|
||||||
|
[sphinx]
|
||||||
|
recipe = collective.recipe.sphinxbuilder
|
||||||
|
source = ${buildout:directory}/doc/source
|
||||||
|
build = ${buildout:directory}/doc
|
||||||
|
interpreter = ${buildout:directory}/bin/pythonpulp
|
||||||
|
outputs = html
|
||||||
|
|
||||||
100
examples/AmericanSteelProblem.py
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
"""
|
||||||
|
The American Steel Problem for the PuLP Modeller
|
||||||
|
|
||||||
|
Authors: Antony Phillips, Dr Stuart Mitchell 2007
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Import PuLP modeller functions
|
||||||
|
from pulp import *
|
||||||
|
|
||||||
|
# List of all the nodes
|
||||||
|
Nodes = ["Youngstown",
|
||||||
|
"Pittsburgh",
|
||||||
|
"Cincinatti",
|
||||||
|
"Kansas City",
|
||||||
|
"Chicago",
|
||||||
|
"Albany",
|
||||||
|
"Houston",
|
||||||
|
"Tempe",
|
||||||
|
"Gary"]
|
||||||
|
|
||||||
|
nodeData = {# NODE Supply Demand
|
||||||
|
"Youngstown": [10000,0],
|
||||||
|
"Pittsburgh": [15000,0],
|
||||||
|
"Cincinatti": [0,0],
|
||||||
|
"Kansas City": [0,0],
|
||||||
|
"Chicago": [0,0],
|
||||||
|
"Albany": [0,3000],
|
||||||
|
"Houston": [0,7000],
|
||||||
|
"Tempe": [0,4000],
|
||||||
|
"Gary": [0,6000]}
|
||||||
|
|
||||||
|
# List of all the arcs
|
||||||
|
Arcs = [("Youngstown","Albany"),
|
||||||
|
("Youngstown","Cincinatti"),
|
||||||
|
("Youngstown","Kansas City"),
|
||||||
|
("Youngstown","Chicago"),
|
||||||
|
("Pittsburgh","Cincinatti"),
|
||||||
|
("Pittsburgh","Kansas City"),
|
||||||
|
("Pittsburgh","Chicago"),
|
||||||
|
("Pittsburgh","Gary"),
|
||||||
|
("Cincinatti","Albany"),
|
||||||
|
("Cincinatti","Houston"),
|
||||||
|
("Kansas City","Houston"),
|
||||||
|
("Kansas City","Tempe"),
|
||||||
|
("Chicago","Tempe"),
|
||||||
|
("Chicago","Gary")]
|
||||||
|
|
||||||
|
arcData = { # ARC Cost Min Max
|
||||||
|
("Youngstown","Albany"): [0.5,0,1000],
|
||||||
|
("Youngstown","Cincinatti"): [0.35,0,3000],
|
||||||
|
("Youngstown","Kansas City"): [0.45,1000,5000],
|
||||||
|
("Youngstown","Chicago"): [0.375,0,5000],
|
||||||
|
("Pittsburgh","Cincinatti"): [0.35,0,2000],
|
||||||
|
("Pittsburgh","Kansas City"): [0.45,2000,3000],
|
||||||
|
("Pittsburgh","Chicago"): [0.4,0,4000],
|
||||||
|
("Pittsburgh","Gary"): [0.45,0,2000],
|
||||||
|
("Cincinatti","Albany"): [0.35,1000,5000],
|
||||||
|
("Cincinatti","Houston"): [0.55,0,6000],
|
||||||
|
("Kansas City","Houston"): [0.375,0,4000],
|
||||||
|
("Kansas City","Tempe"): [0.65,0,4000],
|
||||||
|
("Chicago","Tempe"): [0.6,0,2000],
|
||||||
|
("Chicago","Gary"): [0.12,0,4000]}
|
||||||
|
|
||||||
|
# Splits the dictionaries to be more understandable
|
||||||
|
(supply, demand) = splitDict(nodeData)
|
||||||
|
(costs, mins, maxs) = splitDict(arcData)
|
||||||
|
|
||||||
|
# Creates the boundless Variables as Integers
|
||||||
|
vars = LpVariable.dicts("Route",Arcs,None,None,LpInteger)
|
||||||
|
|
||||||
|
# Creates the upper and lower bounds on the variables
|
||||||
|
for a in Arcs:
|
||||||
|
vars[a].bounds(mins[a], maxs[a])
|
||||||
|
|
||||||
|
# Creates the 'prob' variable to contain the problem data
|
||||||
|
prob = LpProblem("American Steel Problem",LpMinimize)
|
||||||
|
|
||||||
|
# Creates the objective function
|
||||||
|
prob += lpSum([vars[a]* costs[a] for a in Arcs]), "Total Cost of Transport"
|
||||||
|
|
||||||
|
# Creates all problem constraints - this ensures the amount going into each node is at least equal to the amount leaving
|
||||||
|
for n in Nodes:
|
||||||
|
prob += (supply[n]+ lpSum([vars[(i,j)] for (i,j) in Arcs if j == n]) >=
|
||||||
|
demand[n]+ lpSum([vars[(i,j)] for (i,j) in Arcs if i == n])), "Steel Flow Conservation in Node %s"%n
|
||||||
|
|
||||||
|
# The problem data is written to an .lp file
|
||||||
|
prob.writeLP("AmericanSteelProblem.lp")
|
||||||
|
|
||||||
|
# The problem is solved using PuLP's choice of Solver
|
||||||
|
prob.solve()
|
||||||
|
|
||||||
|
# The status of the solution is printed to the screen
|
||||||
|
print "Status:", LpStatus[prob.status]
|
||||||
|
|
||||||
|
# Each of the variables is printed with it's resolved optimum value
|
||||||
|
for v in prob.variables():
|
||||||
|
print v.name, "=", v.varValue
|
||||||
|
|
||||||
|
# The optimised objective function value is printed to the screen
|
||||||
|
print "Total Cost of Transportation = ", value(prob.objective)
|
||||||
71
examples/BeerDistributionProblem.py
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
"""
|
||||||
|
The Beer Distribution Problem for the PuLP Modeller
|
||||||
|
|
||||||
|
Authors: Antony Phillips, Dr Stuart Mitchell 2007
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Import PuLP modeler functions
|
||||||
|
from pulp import *
|
||||||
|
|
||||||
|
# Creates a list of all the supply nodes
|
||||||
|
Warehouses = ["A", "B"]
|
||||||
|
|
||||||
|
# Creates a dictionary for the number of units of supply for each supply node
|
||||||
|
supply = {"A": 1000,
|
||||||
|
"B": 4000}
|
||||||
|
|
||||||
|
# Creates a list of all demand nodes
|
||||||
|
Bars = ["1", "2", "3", "4", "5"]
|
||||||
|
|
||||||
|
# Creates a dictionary for the number of units of demand for each demand node
|
||||||
|
demand = {"1":500,
|
||||||
|
"2":900,
|
||||||
|
"3":1800,
|
||||||
|
"4":200,
|
||||||
|
"5":700,}
|
||||||
|
|
||||||
|
# Creates a list of costs of each transportation path
|
||||||
|
costs = [ #Bars
|
||||||
|
#1 2 3 4 5
|
||||||
|
[2,4,5,2,1],#A Warehouses
|
||||||
|
[3,1,3,2,3] #B
|
||||||
|
]
|
||||||
|
|
||||||
|
# The cost data is made into a dictionary
|
||||||
|
costs = makeDict([Warehouses,Bars],costs,0)
|
||||||
|
|
||||||
|
# Creates the 'prob' variable to contain the problem data
|
||||||
|
prob = LpProblem("Beer Distribution Problem",LpMinimize)
|
||||||
|
|
||||||
|
# Creates a list of tuples containing all the possible routes for transport
|
||||||
|
Routes = [(w,b) for w in Warehouses for b in Bars]
|
||||||
|
|
||||||
|
# A dictionary called 'Vars' is created to contain the referenced variables(the routes)
|
||||||
|
vars = LpVariable.dicts("Route",(Warehouses,Bars),0,None,LpInteger)
|
||||||
|
|
||||||
|
# The objective function is added to 'prob' first
|
||||||
|
prob += lpSum([vars[w][b]*costs[w][b] for (w,b) in Routes]), "Sum_of_Transporting_Costs"
|
||||||
|
|
||||||
|
# The supply maximum constraints are added to prob for each supply node (warehouse)
|
||||||
|
for w in Warehouses:
|
||||||
|
prob += lpSum([vars[w][b] for b in Bars])<=supply[w], "Sum_of_Products_out_of_Warehouse_%s"%w
|
||||||
|
|
||||||
|
# The demand minimum constraints are added to prob for each demand node (bar)
|
||||||
|
for b in Bars:
|
||||||
|
prob += lpSum([vars[w][b] for w in Warehouses])>=demand[b], "Sum_of_Products_into_Bar%s"%b
|
||||||
|
|
||||||
|
# The problem data is written to an .lp file
|
||||||
|
prob.writeLP("BeerDistributionProblem.lp")
|
||||||
|
|
||||||
|
# The problem is solved using PuLP's choice of Solver
|
||||||
|
prob.solve()
|
||||||
|
|
||||||
|
# The status of the solution is printed to the screen
|
||||||
|
print "Status:", LpStatus[prob.status]
|
||||||
|
|
||||||
|
# Each of the variables is printed with it's resolved optimum value
|
||||||
|
for v in prob.variables():
|
||||||
|
print v.name, "=", v.varValue
|
||||||
|
|
||||||
|
# The optimised objective function value is printed to the screen
|
||||||
|
print "Total Cost of Transportation = ", value(prob.objective)
|
||||||
82
examples/BeerDistributionProblem_resolve.py
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
"""
|
||||||
|
The Beer Distribution Problem for the PuLP Modeller
|
||||||
|
Illustrates changing a constraint and solving
|
||||||
|
|
||||||
|
Authors: Antony Phillips, Dr Stuart Mitchell 2007
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Import PuLP modeler functions
|
||||||
|
from pulp import *
|
||||||
|
|
||||||
|
# Creates a list of all the supply nodes
|
||||||
|
Warehouses = ["A", "B"]
|
||||||
|
|
||||||
|
# Creates a dictionary for the number of units of supply for each supply node
|
||||||
|
supply = {"A": 1000,
|
||||||
|
"B": 4000}
|
||||||
|
|
||||||
|
# Creates a list of all demand nodes
|
||||||
|
Bars = ["1", "2", "3", "4", "5"]
|
||||||
|
|
||||||
|
# Creates a dictionary for the number of units of demand for each demand node
|
||||||
|
demand = {"1":500,
|
||||||
|
"2":900,
|
||||||
|
"3":1800,
|
||||||
|
"4":200,
|
||||||
|
"5":700,}
|
||||||
|
|
||||||
|
# Creates a list of costs of each transportation path
|
||||||
|
costs = [ #Bars
|
||||||
|
#1 2 3 4 5
|
||||||
|
[2,4,5,2,1],#A Warehouses
|
||||||
|
[3,1,3,2,3] #B
|
||||||
|
]
|
||||||
|
|
||||||
|
# The cost data is made into a dictionary
|
||||||
|
costs = makeDict([Warehouses,Bars],costs,0)
|
||||||
|
|
||||||
|
# Creates the 'prob' variable to contain the problem data
|
||||||
|
prob = LpProblem("Beer Distribution Problem",LpMinimize)
|
||||||
|
|
||||||
|
# Creates a list of tuples containing all the possible routes for transport
|
||||||
|
Routes = [(w,b) for w in Warehouses for b in Bars]
|
||||||
|
|
||||||
|
# A dictionary called 'Vars' is created to contain the referenced variables(the routes)
|
||||||
|
vars = LpVariable.dicts("Route",(Warehouses,Bars),0,None,LpInteger)
|
||||||
|
|
||||||
|
# The objective function is added to 'prob' first
|
||||||
|
prob += lpSum([vars[w][b]*costs[w][b] for (w,b) in Routes]), "Sum_of_Transporting_Costs"
|
||||||
|
|
||||||
|
# The supply maximum constraints are added to prob for each supply node (warehouse)
|
||||||
|
for w in Warehouses:
|
||||||
|
prob += lpSum([vars[w][b] for b in Bars])<=supply[w], "Sum_of_Products_out_of_Warehouse_%s"%w
|
||||||
|
|
||||||
|
# The demand minimum constraints are added to prob for each demand node (bar)
|
||||||
|
# These constraints are stored for resolve later
|
||||||
|
bar_demand_constraint = {}
|
||||||
|
for b in Bars:
|
||||||
|
constraint = lpSum([vars[w][b] for w in Warehouses])>=demand[b]
|
||||||
|
prob += constraint, "Sum_of_Products_into_Bar_%s"%b
|
||||||
|
bar_demand_constraint[b] = constraint
|
||||||
|
|
||||||
|
# The problem data is written to an .lp file
|
||||||
|
prob.writeLP("BeerDistributionProblem.lp")
|
||||||
|
for demand in range(500, 601, 10):
|
||||||
|
# reoptimise the problem by increasing demand at bar '1'
|
||||||
|
# note the constant is stored as the LHS constant not the RHS of the constraint
|
||||||
|
bar_demand_constraint['1'].constant = - demand
|
||||||
|
#or alternatively,
|
||||||
|
#prob.constraints["Sum_of_Products_into_Bar_1"].constant = - demand
|
||||||
|
|
||||||
|
# The problem is solved using PuLP's choice of Solver
|
||||||
|
prob.solve()
|
||||||
|
|
||||||
|
# The status of the solution is printed to the screen
|
||||||
|
print "Status:", LpStatus[prob.status]
|
||||||
|
|
||||||
|
# Each of the variables is printed with it's resolved optimum value
|
||||||
|
for v in prob.variables():
|
||||||
|
print v.name, "=", v.varValue
|
||||||
|
|
||||||
|
# The optimised objective function value is printed to the screen
|
||||||
|
print "Total Cost of Transportation = ", value(prob.objective)
|
||||||
119
examples/CG.py
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
"""
|
||||||
|
Column Generation Functions
|
||||||
|
|
||||||
|
Authors: Antony Phillips, Dr Stuart Mitchell 2008
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Import PuLP modeler functions
|
||||||
|
from pulp import *
|
||||||
|
|
||||||
|
class Pattern:
|
||||||
|
"""
|
||||||
|
Information on a specific pattern in the SpongeRoll Problem
|
||||||
|
"""
|
||||||
|
cost = 1
|
||||||
|
trimValue = 0.04
|
||||||
|
totalRollLength = 20
|
||||||
|
lenOpts = ["5", "7", "9"]
|
||||||
|
|
||||||
|
def __init__(self, name, lengths = None):
|
||||||
|
self.name = name
|
||||||
|
self.lengthsdict = dict(zip(self.lenOpts,lengths))
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
def trim(self):
|
||||||
|
return Pattern.totalRollLength - sum([int(i)*int(self.lengthsdict[i]) for i in self.lengthsdict])
|
||||||
|
|
||||||
|
def masterSolve(Patterns, rollData, relax = True):
|
||||||
|
|
||||||
|
# The rollData is made into separate dictionaries
|
||||||
|
(rollDemand,surplusPrice) = splitDict(rollData)
|
||||||
|
|
||||||
|
# The variable 'prob' is created
|
||||||
|
prob = LpProblem("Cutting Stock Problem",LpMinimize)
|
||||||
|
|
||||||
|
# vartype represents whether or not the variables are relaxed
|
||||||
|
if relax:
|
||||||
|
vartype = LpContinuous
|
||||||
|
else:
|
||||||
|
vartype = LpInteger
|
||||||
|
|
||||||
|
# The problem variables are created
|
||||||
|
pattVars = LpVariable.dicts("Pattern", Patterns, 0, None, vartype)
|
||||||
|
surplusVars = LpVariable.dicts("Surplus", Pattern.lenOpts, 0, None, vartype)
|
||||||
|
|
||||||
|
# The objective function is entered: (the total number of large rolls used * the cost of each) -
|
||||||
|
# (the value of the surplus stock) - (the value of the trim)
|
||||||
|
prob += lpSum([pattVars[i]*Pattern.cost for i in Patterns]) - lpSum([surplusVars[i]*surplusPrice[i]\
|
||||||
|
for i in Pattern.lenOpts]) - lpSum([pattVars[i]*i.trim()*Pattern.trimValue for i in Patterns])
|
||||||
|
|
||||||
|
# The demand minimum constraint is entered
|
||||||
|
for j in Pattern.lenOpts:
|
||||||
|
prob += lpSum([pattVars[i]*i.lengthsdict[j] for i in Patterns]) - surplusVars[j]>=rollDemand[j],"Min%s"%j
|
||||||
|
|
||||||
|
# The problem is solved
|
||||||
|
prob.solve()
|
||||||
|
|
||||||
|
# The variable values are rounded
|
||||||
|
prob.roundSolution()
|
||||||
|
|
||||||
|
if relax:
|
||||||
|
# Creates a dual variables list
|
||||||
|
duals = {}
|
||||||
|
for name,i in zip(['Min5','Min7','Min9'],Pattern.lenOpts):
|
||||||
|
duals[i] = prob.constraints[name].pi
|
||||||
|
|
||||||
|
return duals
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Creates a dictionary of the variables and their values
|
||||||
|
varsdict = {}
|
||||||
|
for v in prob.variables():
|
||||||
|
varsdict[v.name] = v.varValue
|
||||||
|
|
||||||
|
# The number of rolls of each length in each pattern is printed
|
||||||
|
for i in Patterns:
|
||||||
|
print i, " = %s"%[i.lengthsdict[j] for j in Pattern.lenOpts]
|
||||||
|
|
||||||
|
return value(prob.objective), varsdict
|
||||||
|
|
||||||
|
def subSolve(Patterns, duals):
|
||||||
|
|
||||||
|
# The variable 'prob' is created
|
||||||
|
prob = LpProblem("SubProb",LpMinimize)
|
||||||
|
|
||||||
|
# The problem variables are created
|
||||||
|
vars = LpVariable.dicts("Roll Length", Pattern.lenOpts, 0, None, LpInteger)
|
||||||
|
|
||||||
|
trim = LpVariable("Trim", 0 ,None,LpInteger)
|
||||||
|
|
||||||
|
# The objective function is entered: the reduced cost of a new pattern
|
||||||
|
prob += (Pattern.cost - Pattern.trimValue*trim) - lpSum([vars[i]*duals[i] for i in Pattern.lenOpts]), "Objective"
|
||||||
|
|
||||||
|
# The conservation of length constraint is entered
|
||||||
|
prob += lpSum([vars[i]*int(i) for i in Pattern.lenOpts]) + trim == Pattern.totalRollLength, "lengthEquate"
|
||||||
|
|
||||||
|
# The problem is solved
|
||||||
|
prob.solve()
|
||||||
|
|
||||||
|
# The variable values are rounded
|
||||||
|
prob.roundSolution()
|
||||||
|
|
||||||
|
# The new pattern is written to a dictionary
|
||||||
|
varsdict = {}
|
||||||
|
newPattern = {}
|
||||||
|
for v in prob.variables():
|
||||||
|
varsdict[v.name] = v.varValue
|
||||||
|
for i,j in zip(Pattern.lenOpts,["Roll_Length_5","Roll_Length_7","Roll_Length_9"]):
|
||||||
|
newPattern[i] = int(varsdict[j])
|
||||||
|
|
||||||
|
# Check if there are more patterns which would reduce the master LP objective function further
|
||||||
|
if value(prob.objective) < -10**-5:
|
||||||
|
morePatterns = True # continue adding patterns
|
||||||
|
Patterns += [Pattern("P" + str(len(Patterns)), [newPattern[i] for i in ["5","7","9"]])]
|
||||||
|
else:
|
||||||
|
morePatterns = False # all patterns have been added
|
||||||
|
|
||||||
|
return Patterns, morePatterns
|
||||||
143
examples/CGcolumnwise.py
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
"""
|
||||||
|
Columnwise Column Generation Functions
|
||||||
|
|
||||||
|
Authors: Antony Phillips, Dr Stuart Mitchell 2008
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Import PuLP modeler functions
|
||||||
|
from pulp import *
|
||||||
|
|
||||||
|
class Pattern:
|
||||||
|
"""
|
||||||
|
Information on a specific pattern in the SpongeRoll Problem
|
||||||
|
"""
|
||||||
|
cost = 1
|
||||||
|
trimValue = 0.04
|
||||||
|
totalRollLength = 20
|
||||||
|
lenOpts = ["5", "7", "9"]
|
||||||
|
numPatterns = 0
|
||||||
|
|
||||||
|
def __init__(self, name, lengths = None):
|
||||||
|
self.name = name
|
||||||
|
self.lengthsdict = dict(zip(self.lenOpts,lengths))
|
||||||
|
Pattern.numPatterns += 1
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
def trim(self):
|
||||||
|
return Pattern.totalRollLength - sum([int(i)*int(self.lengthsdict[i]) for i in self.lengthsdict])
|
||||||
|
|
||||||
|
def createMaster():
|
||||||
|
|
||||||
|
rollData = {#Length Demand SalePrice
|
||||||
|
"5": [150, 0.25],
|
||||||
|
"7": [200, 0.33],
|
||||||
|
"9": [300, 0.40]}
|
||||||
|
|
||||||
|
(rollDemand,surplusPrice) = splitDict(rollData)
|
||||||
|
|
||||||
|
# The variable 'prob' is created
|
||||||
|
prob = LpProblem("MasterSpongeRollProblem",LpMinimize)
|
||||||
|
|
||||||
|
# The variable 'obj' is created and set as the LP's objective function
|
||||||
|
obj = LpConstraintVar("Obj")
|
||||||
|
prob.setObjective(obj)
|
||||||
|
|
||||||
|
# The constraints are initialised and added to prob
|
||||||
|
constraints = {}
|
||||||
|
for l in Pattern.lenOpts:
|
||||||
|
constraints[l]= LpConstraintVar("Min" + str(l), LpConstraintGE, rollDemand[l])
|
||||||
|
prob += constraints[l]
|
||||||
|
|
||||||
|
# The surplus variables are created
|
||||||
|
surplusVars = []
|
||||||
|
for i in Pattern.lenOpts:
|
||||||
|
surplusVars += [LpVariable("Surplus "+ i,0,None,LpContinuous, -surplusPrice[i] * obj - constraints[i])]
|
||||||
|
|
||||||
|
return prob,obj,constraints
|
||||||
|
|
||||||
|
def addPatterns(obj,constraints,newPatterns):
|
||||||
|
|
||||||
|
# A list called Patterns is created to contain all the Pattern class
|
||||||
|
# objects created in this function call
|
||||||
|
Patterns = []
|
||||||
|
for i in newPatterns:
|
||||||
|
|
||||||
|
# The new patterns are checked to see that their length does not exceed
|
||||||
|
# the total roll length
|
||||||
|
lsum = 0
|
||||||
|
for j,k in zip(i,Pattern.lenOpts):
|
||||||
|
lsum += j * int(k)
|
||||||
|
if lsum > Pattern.totalRollLength:
|
||||||
|
raise "Length Options too large for Roll"
|
||||||
|
|
||||||
|
# The number of rolls of each length in each new pattern is printed
|
||||||
|
print "P"+str(Pattern.numPatterns),"=",i
|
||||||
|
|
||||||
|
# The patterns are instantiated as Pattern objects
|
||||||
|
Patterns += [Pattern("P" + str(Pattern.numPatterns),i)]
|
||||||
|
|
||||||
|
# The pattern variables are created
|
||||||
|
pattVars = []
|
||||||
|
for i in Patterns:
|
||||||
|
pattVars += [LpVariable("Pattern "+i.name,0,None,LpContinuous, (i.cost - Pattern.trimValue*i.trim()) * obj\
|
||||||
|
+ lpSum([constraints[l]*i.lengthsdict[l] for l in Pattern.lenOpts]))]
|
||||||
|
|
||||||
|
def masterSolve(prob,relax = True):
|
||||||
|
|
||||||
|
# Unrelaxes the Integer Constraint
|
||||||
|
if not relax:
|
||||||
|
for v in prob.variables():
|
||||||
|
v.cat = LpInteger
|
||||||
|
|
||||||
|
# The problem is solved and rounded
|
||||||
|
prob.solve()
|
||||||
|
prob.roundSolution()
|
||||||
|
|
||||||
|
if relax:
|
||||||
|
# A dictionary of dual variable values is returned
|
||||||
|
duals = {}
|
||||||
|
for i,name in zip(Pattern.lenOpts,["Min5","Min7","Min9"]):
|
||||||
|
duals[i] = prob.constraints[name].pi
|
||||||
|
return duals
|
||||||
|
else:
|
||||||
|
# A dictionary of variable values and the objective value are returned
|
||||||
|
varsdict = {}
|
||||||
|
for v in prob.variables():
|
||||||
|
varsdict[v.name] = v.varValue
|
||||||
|
|
||||||
|
return value(prob.objective), varsdict
|
||||||
|
|
||||||
|
def subSolve(duals):
|
||||||
|
|
||||||
|
# The variable 'prob' is created
|
||||||
|
prob = LpProblem("SubProb",LpMinimize)
|
||||||
|
|
||||||
|
# The problem variables are created
|
||||||
|
vars = LpVariable.dicts("Roll Length", Pattern.lenOpts, 0, None, LpInteger)
|
||||||
|
|
||||||
|
trim = LpVariable("Trim", 0 ,None,LpInteger)
|
||||||
|
|
||||||
|
# The objective function is entered: the reduced cost of a new pattern
|
||||||
|
prob += (Pattern.cost - Pattern.trimValue*trim) - lpSum([vars[i]*duals[i] for i in Pattern.lenOpts]), "Objective"
|
||||||
|
|
||||||
|
# The conservation of length constraint is entered
|
||||||
|
prob += lpSum([vars[i]*int(i) for i in Pattern.lenOpts]) + trim == Pattern.totalRollLength, "lengthEquate"
|
||||||
|
|
||||||
|
# The problem is solved
|
||||||
|
prob.solve()
|
||||||
|
|
||||||
|
# The variable values are rounded
|
||||||
|
prob.roundSolution()
|
||||||
|
|
||||||
|
newPatterns = []
|
||||||
|
# Check if there are more patterns which would reduce the master LP objective function further
|
||||||
|
if value(prob.objective) < -10**-5:
|
||||||
|
varsdict = {}
|
||||||
|
for v in prob.variables():
|
||||||
|
varsdict[v.name] = v.varValue
|
||||||
|
# Adds the new pattern to the newPatterns list
|
||||||
|
newPatterns += [[int(varsdict["Roll_Length_5"]),int(varsdict["Roll_Length_7"]),int(varsdict["Roll_Length_9"])]]
|
||||||
|
|
||||||
|
return newPatterns
|
||||||
91
examples/ComputerPlantProblem.py
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
"""
|
||||||
|
The Computer Plant Problem for the PuLP Modeller
|
||||||
|
|
||||||
|
Authors: Antony Phillips, Dr Stuart Mitchell 2007
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Import PuLP modeler functions
|
||||||
|
from pulp import *
|
||||||
|
|
||||||
|
# Creates a list of all the supply nodes
|
||||||
|
Plants = ["San Francisco",
|
||||||
|
"Los Angeles",
|
||||||
|
"Phoenix",
|
||||||
|
"Denver"]
|
||||||
|
|
||||||
|
# Creates a dictionary of lists for the number of units of supply at
|
||||||
|
# each plant and the fixed cost of running each plant
|
||||||
|
supplyData = {#Plant Supply Fixed Cost
|
||||||
|
"San Francisco":[1700, 70000],
|
||||||
|
"Los Angeles" :[2000, 70000],
|
||||||
|
"Phoenix" :[1700, 65000],
|
||||||
|
"Denver" :[2000, 70000]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Creates a list of all demand nodes
|
||||||
|
Stores = ["San Diego",
|
||||||
|
"Barstow",
|
||||||
|
"Tucson",
|
||||||
|
"Dallas"]
|
||||||
|
|
||||||
|
# Creates a dictionary for the number of units of demand at each store
|
||||||
|
demand = { #Store Demand
|
||||||
|
"San Diego":1700,
|
||||||
|
"Barstow" :1000,
|
||||||
|
"Tucson" :1500,
|
||||||
|
"Dallas" :1200
|
||||||
|
}
|
||||||
|
|
||||||
|
# Creates a list of costs for each transportation path
|
||||||
|
costs = [ #Stores
|
||||||
|
#SD BA TU DA
|
||||||
|
[5, 3, 2, 6], #SF
|
||||||
|
[4, 7, 8, 10],#LA Plants
|
||||||
|
[6, 5, 3, 8], #PH
|
||||||
|
[9, 8, 6, 5] #DE
|
||||||
|
]
|
||||||
|
|
||||||
|
# Creates a list of tuples containing all the possible routes for transport
|
||||||
|
Routes = [(p,s) for p in Plants for s in Stores]
|
||||||
|
|
||||||
|
# Splits the dictionaries to be more understandable
|
||||||
|
(supply,fixedCost) = splitDict(supplyData)
|
||||||
|
|
||||||
|
# The cost data is made into a dictionary
|
||||||
|
costs = makeDict([Plants,Stores],costs,0)
|
||||||
|
|
||||||
|
# Creates the problem variables of the Flow on the Arcs
|
||||||
|
flow = LpVariable.dicts("Route",(Plants,Stores),0,None,LpInteger)
|
||||||
|
|
||||||
|
# Creates the master problem variables of whether to build the Plants or not
|
||||||
|
build = LpVariable.dicts("BuildaPlant",Plants,0,1,LpInteger)
|
||||||
|
|
||||||
|
# Creates the 'prob' variable to contain the problem data
|
||||||
|
prob = LpProblem("Computer Plant Problem",LpMinimize)
|
||||||
|
|
||||||
|
# The objective function is added to prob - The sum of the transportation costs and the building fixed costs
|
||||||
|
prob += lpSum([flow[p][s]*costs[p][s] for (p,s) in Routes])+lpSum([fixedCost[p]*build[p] for p in Plants]),"Total Costs"
|
||||||
|
|
||||||
|
# The Supply maximum constraints are added for each supply node (plant)
|
||||||
|
for p in Plants:
|
||||||
|
prob += lpSum([flow[p][s] for s in Stores])<=supply[p]*build[p], "Sum of Products out of Plant %s"%p
|
||||||
|
|
||||||
|
# The Demand minimum constraints are added for each demand node (store)
|
||||||
|
for s in Stores:
|
||||||
|
prob += lpSum([flow[p][s] for p in Plants])>=demand[s], "Sum of Products into Stores %s"%s
|
||||||
|
|
||||||
|
# The problem data is written to an .lp file
|
||||||
|
prob.writeLP("ComputerPlantProblem.lp")
|
||||||
|
|
||||||
|
# The problem is solved using PuLP's choice of Solver
|
||||||
|
prob.solve()
|
||||||
|
|
||||||
|
# The status of the solution is printed to the screen
|
||||||
|
print "Status:", LpStatus[prob.status]
|
||||||
|
|
||||||
|
# Each of the variables is printed with it's resolved optimum value
|
||||||
|
for v in prob.variables():
|
||||||
|
print v.name, "=", v.varValue
|
||||||
|
|
||||||
|
# The optimised objective function value is printed to the screen
|
||||||
|
print "Total Costs = ", value(prob.objective)
|
||||||
61
examples/SpongeRollProblem1.py
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
"""
|
||||||
|
The Simplified Sponge Roll Problem for the PuLP Modeller
|
||||||
|
|
||||||
|
Authors: Antony Phillips, Dr Stuart Mitchell 2007
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Import PuLP modeler functions
|
||||||
|
from pulp import *
|
||||||
|
|
||||||
|
# A list of all the roll lengths is created
|
||||||
|
LenOpts = ["5","7","9"]
|
||||||
|
|
||||||
|
# A dictionary of the demand for each roll length is created
|
||||||
|
rollDemand = {"5":150,
|
||||||
|
"7":200,
|
||||||
|
"9":300}
|
||||||
|
|
||||||
|
# A list of all the patterns is created
|
||||||
|
PatternNames = ["A","B","C"]
|
||||||
|
|
||||||
|
# Creates a list of the number of rolls in each pattern for each different roll length
|
||||||
|
patterns = [#A B C
|
||||||
|
[0,2,2],# 5
|
||||||
|
[1,1,0],# 7
|
||||||
|
[1,0,1] # 9
|
||||||
|
]
|
||||||
|
|
||||||
|
# The cost of each 20cm long sponge roll used
|
||||||
|
cost = 1
|
||||||
|
|
||||||
|
# The pattern data is made into a dictionary
|
||||||
|
patterns = makeDict([LenOpts,PatternNames],patterns,0)
|
||||||
|
|
||||||
|
# The problem variables of the number of each pattern to make are created
|
||||||
|
vars = LpVariable.dicts("Patt",PatternNames,0,None,LpInteger)
|
||||||
|
|
||||||
|
# The variable 'prob' is created
|
||||||
|
prob = LpProblem("Cutting Stock Problem",LpMinimize)
|
||||||
|
|
||||||
|
# The objective function is entered: the total number of large rolls used * the fixed cost of each
|
||||||
|
prob += lpSum([vars[i]*cost for i in PatternNames]),"Production Cost"
|
||||||
|
|
||||||
|
# The demand minimum constraint is entered
|
||||||
|
for i in LenOpts:
|
||||||
|
prob += lpSum([vars[j]*patterns[i][j] for j in PatternNames])>=rollDemand[i],"Ensuring enough %s cm rolls"%i
|
||||||
|
|
||||||
|
# The problem data is written to an .lp file
|
||||||
|
prob.writeLP("SpongeRollProblem.lp")
|
||||||
|
|
||||||
|
# The problem is solved using PuLP's choice of Solver
|
||||||
|
prob.solve()
|
||||||
|
|
||||||
|
# The status of the solution is printed to the screen
|
||||||
|
print "Status:", LpStatus[prob.status]
|
||||||
|
|
||||||
|
# Each of the variables is printed with it's resolved optimum value
|
||||||
|
for v in prob.variables():
|
||||||
|
print v.name, "=", v.varValue
|
||||||
|
|
||||||
|
# The optimised objective function value is printed to the screen
|
||||||
|
print "Production Costs = ", value(prob.objective)
|
||||||
78
examples/SpongeRollProblem2.py
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
"""
|
||||||
|
The Simplified Sponge Roll Problem with Surplus and Trim for the PuLP Modeller
|
||||||
|
|
||||||
|
Authors: Antony Phillips, Dr Stuart Mitchell 2007
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Import PuLP modeler functions
|
||||||
|
from pulp import *
|
||||||
|
|
||||||
|
# A list of all the roll lengths is created
|
||||||
|
LenOpts = ["5","7","9"]
|
||||||
|
|
||||||
|
rollData = {#Length Demand SalePrice
|
||||||
|
"5": [150, 0.25],
|
||||||
|
"7": [200, 0.33],
|
||||||
|
"9": [300, 0.40]}
|
||||||
|
|
||||||
|
# A list of all the patterns is created
|
||||||
|
PatternNames = ["A","B","C"]
|
||||||
|
|
||||||
|
# Creates a list of the number of rolls in each pattern for each different roll length
|
||||||
|
patterns = [#A B C
|
||||||
|
[0,2,2],# 5
|
||||||
|
[1,1,0],# 7
|
||||||
|
[1,0,1] # 9
|
||||||
|
]
|
||||||
|
|
||||||
|
# A dictionary of the number of cms of trim in each pattern is created
|
||||||
|
trim = {"A": 4,
|
||||||
|
"B": 2,
|
||||||
|
"C": 1}
|
||||||
|
|
||||||
|
# The cost of each 20cm long sponge roll used
|
||||||
|
cost = 1
|
||||||
|
|
||||||
|
# The sale value of each cm of trim
|
||||||
|
trimValue = 0.04
|
||||||
|
|
||||||
|
# The rollData is made into separate dictionaries
|
||||||
|
(rollDemand,surplusPrice) = splitDict(rollData)
|
||||||
|
|
||||||
|
# The pattern data is made into a dictionary
|
||||||
|
patterns = makeDict([LenOpts,PatternNames],patterns,0)
|
||||||
|
|
||||||
|
# The problem variables of the number of each pattern to make are created
|
||||||
|
pattVars = LpVariable.dicts("Patt",PatternNames,0,None,LpInteger)
|
||||||
|
|
||||||
|
# The problem variables of the number of surplus rolls for each length are created
|
||||||
|
surplusVars = LpVariable.dicts("Surp",LenOpts,0,None,LpInteger)
|
||||||
|
|
||||||
|
# The variable 'prob' is created
|
||||||
|
prob = LpProblem("Cutting Stock Problem",LpMinimize)
|
||||||
|
|
||||||
|
# The objective function is entered: the total number of large rolls used * the fixed cost of each minus the surplus
|
||||||
|
# sales and the trim sales
|
||||||
|
prob += lpSum([pattVars[i]*cost for i in PatternNames]) - lpSum([surplusVars[i]*surplusPrice[i] for i in LenOpts]) \
|
||||||
|
- lpSum([pattVars[i]*trim[i]*trimValue for i in PatternNames]),"Net Production Cost"
|
||||||
|
|
||||||
|
# The demand minimum constraint is entered
|
||||||
|
for i in LenOpts:
|
||||||
|
prob += lpSum([pattVars[j]*patterns[i][j] for j in PatternNames]) - surplusVars[i]\
|
||||||
|
>= rollDemand[i],"Ensuring enough %s cm rolls"%i
|
||||||
|
|
||||||
|
# The problem data is written to an .lp file
|
||||||
|
prob.writeLP("SpongeRollProblem.lp")
|
||||||
|
|
||||||
|
# The problem is solved using PuLP's choice of Solver
|
||||||
|
prob.solve()
|
||||||
|
|
||||||
|
# The status of the solution is printed to the screen
|
||||||
|
print "Status:", LpStatus[prob.status]
|
||||||
|
|
||||||
|
# Each of the variables is printed with it's resolved optimum value
|
||||||
|
for v in prob.variables():
|
||||||
|
print v.name, "=", v.varValue
|
||||||
|
|
||||||
|
# The optimised objective function value is printed to the screen
|
||||||
|
print "Production Costs = ", value(prob.objective)
|
||||||
135
examples/SpongeRollProblem3.py
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
"""
|
||||||
|
The Full Sponge Roll Problem for the PuLP Modeller
|
||||||
|
|
||||||
|
Authors: Antony Phillips, Dr Stuart Mitchell 2007
|
||||||
|
"""
|
||||||
|
|
||||||
|
def calculatePatterns(totalRollLength,lenOpts,head):
|
||||||
|
"""
|
||||||
|
Recursively calculates the list of options lists for a cutting stock problem. The input
|
||||||
|
'tlist' is a pointer, and will be the output of the function call.
|
||||||
|
|
||||||
|
The inputs are:
|
||||||
|
totalRollLength - the length of the roll
|
||||||
|
lenOpts - a list of the sizes of remaining cutting options
|
||||||
|
head - the current list that has been passed down though the recusion
|
||||||
|
|
||||||
|
Returns the list of patterns
|
||||||
|
|
||||||
|
Authors: Bojan Blazevic, Dr Stuart Mitchell 2007
|
||||||
|
"""
|
||||||
|
if lenOpts:
|
||||||
|
patterns =[]
|
||||||
|
#take the first option off lenOpts
|
||||||
|
opt = lenOpts[0]
|
||||||
|
for rep in range(int(totalRollLength/opt)+1):
|
||||||
|
#reduce the length
|
||||||
|
l = totalRollLength - rep*opt
|
||||||
|
h = head[:]
|
||||||
|
h.append(rep)
|
||||||
|
|
||||||
|
patterns.extend(calculatePatterns(l, lenOpts[1:], h))
|
||||||
|
else:
|
||||||
|
#end of the recursion
|
||||||
|
patterns = [head]
|
||||||
|
return patterns
|
||||||
|
|
||||||
|
def makePatterns(totalRollLength,lenOpts):
|
||||||
|
"""
|
||||||
|
Makes the different cutting patterns for a cutting stock problem.
|
||||||
|
|
||||||
|
The inputs are:
|
||||||
|
totalRollLength : the length of the roll
|
||||||
|
lenOpts: a list of the sizes of cutting options as strings
|
||||||
|
|
||||||
|
Authors: Antony Phillips, Dr Stuart Mitchell 2007
|
||||||
|
"""
|
||||||
|
|
||||||
|
# calculatePatterns is called to create a list of the feasible cutting options in 'tlist'
|
||||||
|
patterns = calculatePatterns(totalRollLength,lenOpts,[])
|
||||||
|
|
||||||
|
# The list 'PatternNames' is created
|
||||||
|
PatternNames = []
|
||||||
|
for i in range(len(patterns)):
|
||||||
|
PatternNames += ["P"+str(i)]
|
||||||
|
|
||||||
|
# The amount of trim (unused material) for each pattern is calculated and added to the dictionary
|
||||||
|
# 'trim', with the reference key of the pattern name.
|
||||||
|
trim = {}
|
||||||
|
for name,pattern in zip(PatternNames,patterns):
|
||||||
|
ssum = 0
|
||||||
|
for rep,l in zip(pattern,lenOpts):
|
||||||
|
ssum += rep*l
|
||||||
|
trim[name] = totalRollLength - ssum
|
||||||
|
# The different cutting lengths are printed, and the number of each roll of that length in each
|
||||||
|
# pattern is printed below. This is so the user can see what each pattern contains.
|
||||||
|
print "Lens: %s" %lenOpts
|
||||||
|
for name,pattern in zip(PatternNames,patterns):
|
||||||
|
print name + " = %s"%pattern
|
||||||
|
|
||||||
|
return (PatternNames,patterns,trim)
|
||||||
|
|
||||||
|
|
||||||
|
# Import PuLP modeler functions
|
||||||
|
from pulp import *
|
||||||
|
|
||||||
|
# The Total Roll Length is entered
|
||||||
|
totalRollLength = 20
|
||||||
|
|
||||||
|
# The cost of each 20cm long sponge roll used
|
||||||
|
cost = 1
|
||||||
|
|
||||||
|
# The sale value of each cm of trim
|
||||||
|
trimValue = 0.04
|
||||||
|
|
||||||
|
# A list of all the roll lengths is created
|
||||||
|
LenOpts = ["5","7","9"]
|
||||||
|
|
||||||
|
rollData = {#Length Demand SalePrice
|
||||||
|
"5": [150, 0.25],
|
||||||
|
"7": [200, 0.33],
|
||||||
|
"9": [300, 0.40]}
|
||||||
|
|
||||||
|
# The pattern names and the patterns are created as lists, and the associated trim with each pattern
|
||||||
|
# is created as a dictionary. The inputs are the total roll length and the list (as integers) of
|
||||||
|
# cutting options.
|
||||||
|
(PatternNames,patterns,trim) = makePatterns(totalRollLength,[int(l) for l in LenOpts])
|
||||||
|
|
||||||
|
# The RollData is made into separate dictionaries
|
||||||
|
(rollDemand,surplusPrice) = splitDict(rollData)
|
||||||
|
|
||||||
|
# The pattern data is made into a dictionary so it can be called by patterns["7"]["P3"] for example.
|
||||||
|
# This will return the number of rolls of length "7" in pattern "P3"
|
||||||
|
patterns = makeDict([PatternNames,LenOpts],patterns,0)
|
||||||
|
|
||||||
|
# The variable 'prob' is created
|
||||||
|
prob = LpProblem("Cutting Stock Problem",LpMinimize)
|
||||||
|
|
||||||
|
# The problem variables of the number of each pattern to make are created
|
||||||
|
pattVars = LpVariable.dicts("Patt",PatternNames,0,None,LpInteger)
|
||||||
|
|
||||||
|
# The problem variables of the number of surplus rolls for each length are created
|
||||||
|
surplusVars = LpVariable.dicts("Surp",LenOpts,0,None,LpInteger)
|
||||||
|
|
||||||
|
# The objective function is entered: (the total number of large rolls used * the cost of each) - (the value of the surplus stock) - (the value of the trim)
|
||||||
|
prob += lpSum([pattVars[i]*cost for i in PatternNames]) - lpSum([surplusVars[i]*surplusPrice[i] for i in LenOpts]) - lpSum([pattVars[i]*trim[i]*trimValue for i in PatternNames]),"Net Production Cost"
|
||||||
|
|
||||||
|
# The demand minimum constraint is entered
|
||||||
|
for j in LenOpts:
|
||||||
|
prob += lpSum([pattVars[i]*patterns[i][j] for i in PatternNames]) - surplusVars[j]>=rollDemand[j],"Ensuring enough %s cm rolls"%j
|
||||||
|
|
||||||
|
# The problem data is written to an .lp file
|
||||||
|
prob.writeLP("SpongeRollProblem.lp")
|
||||||
|
|
||||||
|
# The problem is solved using PuLP's choice of Solver
|
||||||
|
prob.solve()
|
||||||
|
|
||||||
|
# The status of the solution is printed to the screen
|
||||||
|
print "Status:", LpStatus[prob.status]
|
||||||
|
|
||||||
|
# Each of the variables is printed with it's resolved optimum value
|
||||||
|
for v in prob.variables():
|
||||||
|
print v.name, "=", v.varValue
|
||||||
|
|
||||||
|
# The optimised objective function value is printed to the screen
|
||||||
|
print "Production Costs = ", value(prob.objective)
|
||||||
137
examples/SpongeRollProblem4.py
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
"""
|
||||||
|
The Full Sponge Roll Problem using Classes for the PuLP Modeller
|
||||||
|
|
||||||
|
Authors: Antony Phillips, Dr Stuart Mitchell 2007
|
||||||
|
"""
|
||||||
|
|
||||||
|
def calculatePatterns(totalRollLength,lenOpts,head):
|
||||||
|
"""
|
||||||
|
Recursively calculates the list of options lists for a cutting stock problem. The input
|
||||||
|
'tlist' is a pointer, and will be the output of the function call.
|
||||||
|
|
||||||
|
The inputs are:
|
||||||
|
totalRollLength - the length of the roll
|
||||||
|
lenOpts - a list of the sizes of remaining cutting options
|
||||||
|
head - the current list that has been passed down though the recusion
|
||||||
|
|
||||||
|
Returns the list of patterns
|
||||||
|
|
||||||
|
Authors: Bojan Blazevic, Dr Stuart Mitchell 2007
|
||||||
|
"""
|
||||||
|
if lenOpts:
|
||||||
|
patterns =[]
|
||||||
|
#take the first option off lenOpts
|
||||||
|
opt = lenOpts[0]
|
||||||
|
for rep in range(int(totalRollLength/opt)+1):
|
||||||
|
#reduce the length
|
||||||
|
l = totalRollLength - rep*opt
|
||||||
|
h = head[:]
|
||||||
|
h.append(rep)
|
||||||
|
|
||||||
|
patterns.extend(calculatePatterns(l, lenOpts[1:], h))
|
||||||
|
else:
|
||||||
|
#end of the recursion
|
||||||
|
patterns = [head]
|
||||||
|
return patterns
|
||||||
|
|
||||||
|
def makePatterns(totalRollLength,lenOpts):
|
||||||
|
"""
|
||||||
|
Makes the different cutting patterns for a cutting stock problem.
|
||||||
|
|
||||||
|
The inputs are:
|
||||||
|
totalRollLength : the length of the roll
|
||||||
|
lenOpts: a list of the sizes of cutting options as strings
|
||||||
|
|
||||||
|
Authors: Antony Phillips, Dr Stuart Mitchell 2007
|
||||||
|
"""
|
||||||
|
|
||||||
|
# calculatePatterns is called to create a list of the feasible cutting options in 'tlist'
|
||||||
|
patternslist = calculatePatterns(totalRollLength,lenOpts,[])
|
||||||
|
|
||||||
|
# The list 'PatternNames' is created
|
||||||
|
PatternNames = []
|
||||||
|
for i in range(len(patternslist)):
|
||||||
|
PatternNames += ["P"+str(i)]
|
||||||
|
|
||||||
|
#Patterns = [0 for i in range(len(PatternNames))]
|
||||||
|
Patterns = []
|
||||||
|
|
||||||
|
for i,j in enumerate(PatternNames):
|
||||||
|
Patterns += [Pattern(j, patternslist[i])]
|
||||||
|
|
||||||
|
# The different cutting lengths are printed, and the number of each roll of that length in each
|
||||||
|
# pattern is printed below. This is so the user can see what each pattern contains.
|
||||||
|
print "Lens: %s" %lenOpts
|
||||||
|
for i in Patterns:
|
||||||
|
print i, " = %s"%[i.lengthsdict[j] for j in lenOpts]
|
||||||
|
|
||||||
|
return Patterns
|
||||||
|
|
||||||
|
|
||||||
|
class Pattern:
|
||||||
|
"""
|
||||||
|
Information on a specific pattern in the SpongeRoll Problem
|
||||||
|
"""
|
||||||
|
cost = 1
|
||||||
|
trimValue = 0.04
|
||||||
|
totalRollLength = 20
|
||||||
|
lenOpts = [5, 7, 9]
|
||||||
|
|
||||||
|
def __init__(self,name,lengths = None):
|
||||||
|
self.name = name
|
||||||
|
self.lengthsdict = dict(zip(self.lenOpts,lengths))
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
def trim(self):
|
||||||
|
return Pattern.totalRollLength - sum([int(i)*self.lengthsdict[i] for i in self.lengthsdict])
|
||||||
|
|
||||||
|
|
||||||
|
# Import PuLP modeler functions
|
||||||
|
from pulp import *
|
||||||
|
|
||||||
|
rollData = {#Length Demand SalePrice
|
||||||
|
5: [150, 0.25],
|
||||||
|
7: [200, 0.33],
|
||||||
|
9: [300, 0.40]}
|
||||||
|
|
||||||
|
# The pattern names and the patterns are created as lists, and the associated trim with each pattern
|
||||||
|
# is created as a dictionary. The inputs are the total roll length and the list (as integers) of
|
||||||
|
# cutting options.
|
||||||
|
Patterns = makePatterns(Pattern.totalRollLength,Pattern.lenOpts)
|
||||||
|
|
||||||
|
# The rollData is made into separate dictionaries
|
||||||
|
(rollDemand,surplusPrice) = splitDict(rollData)
|
||||||
|
|
||||||
|
# The variable 'prob' is created
|
||||||
|
prob = LpProblem("Cutting Stock Problem",LpMinimize)
|
||||||
|
|
||||||
|
# The problem variables of the number of each pattern to make are created
|
||||||
|
pattVars = LpVariable.dicts("Patt",Patterns,0,None,LpInteger)
|
||||||
|
|
||||||
|
# The problem variables of the number of surplus rolls for each length are created
|
||||||
|
surplusVars = LpVariable.dicts("Surp",Pattern.lenOpts,0,None,LpInteger)
|
||||||
|
|
||||||
|
# The objective function is entered: (the total number of large rolls used * the cost of each) - (the value of the surplus stock) - (the value of the trim)
|
||||||
|
prob += lpSum([pattVars[i]*Pattern.cost for i in Patterns]) - lpSum([surplusVars[i]*surplusPrice[i] for i in Pattern.lenOpts]) - lpSum([pattVars[i]*i.trim()*Pattern.trimValue for i in Patterns]),"Net Production Cost"
|
||||||
|
|
||||||
|
# The demand minimum constraint is entered
|
||||||
|
for j in Pattern.lenOpts:
|
||||||
|
prob += lpSum([pattVars[i]*i.lengthsdict[j] for i in Patterns]) - surplusVars[j] >= rollDemand[j],"Ensuring enough %s cm rolls"%j
|
||||||
|
|
||||||
|
# The problem data is written to an .lp file
|
||||||
|
prob.writeLP("SpongeRollProblem.lp")
|
||||||
|
|
||||||
|
# The problem is solved
|
||||||
|
prob.solve()
|
||||||
|
|
||||||
|
# The status of the solution is printed to the screen
|
||||||
|
print "Status:", LpStatus[prob.status]
|
||||||
|
|
||||||
|
# Each of the variables is printed with it's resolved optimum value
|
||||||
|
for v in prob.variables():
|
||||||
|
print v.name, "=", v.varValue
|
||||||
|
|
||||||
|
# The optimised objective function value is printed to the screen
|
||||||
|
print "Production Costs = ", value(prob.objective)
|
||||||
43
examples/SpongeRollProblem5.py
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
"""
|
||||||
|
The Sponge Roll Problem with Column Generation for the PuLP Modeller
|
||||||
|
|
||||||
|
Authors: Antony Phillips, Dr Stuart Mitchell 2008
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Import Column Generation functions
|
||||||
|
from CG import *
|
||||||
|
|
||||||
|
# The roll data is created
|
||||||
|
rollData = {#Length Demand SalePrice
|
||||||
|
"5": [150, 0.25],
|
||||||
|
"7": [200, 0.33],
|
||||||
|
"9": [300, 0.40]}
|
||||||
|
|
||||||
|
# The boolean variable morePatterns is set to True to test for more patterns
|
||||||
|
morePatterns = True
|
||||||
|
|
||||||
|
# A list of starting patterns is created
|
||||||
|
patternslist = [[4,0,0],[0,2,0],[0,0,2]]
|
||||||
|
|
||||||
|
# The starting patterns are instantiated with the Pattern class
|
||||||
|
Patterns = []
|
||||||
|
for i in patternslist:
|
||||||
|
Patterns += [Pattern("P" + str(len(Patterns)), i)]
|
||||||
|
|
||||||
|
# This loop will be repeated until morePatterns is set to False
|
||||||
|
while morePatterns == True:
|
||||||
|
|
||||||
|
# Solve the problem as a Relaxed LP
|
||||||
|
duals = masterSolve(Patterns, rollData)
|
||||||
|
|
||||||
|
# Find another pattern
|
||||||
|
Patterns, morePatterns = subSolve(Patterns, duals)
|
||||||
|
|
||||||
|
# Re-solve as an Integer Problem
|
||||||
|
solution, varsdict = masterSolve(Patterns, rollData, relax = False)
|
||||||
|
|
||||||
|
# Display Solution
|
||||||
|
for i,j in varsdict.items():
|
||||||
|
print i, "=", j
|
||||||
|
|
||||||
|
print "objective = ", solution
|
||||||
33
examples/SpongeRollProblem6.py
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
"""
|
||||||
|
The Sponge Roll Problem with Columnwise Column Generation for the PuLP Modeller
|
||||||
|
|
||||||
|
Authors: Antony Phillips, Dr Stuart Mitchell 2008
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Import Column Generation functions
|
||||||
|
from CGcolumnwise import *
|
||||||
|
|
||||||
|
# The Master Problem is created
|
||||||
|
prob, obj, constraints = createMaster()
|
||||||
|
|
||||||
|
# A list of starting patterns is created
|
||||||
|
newPatterns = [[1,0,0],[0,1,0],[0,0,1]]
|
||||||
|
|
||||||
|
# New patterns will be added until newPatterns is an empty list
|
||||||
|
while newPatterns:
|
||||||
|
# The new patterns are added to the problem
|
||||||
|
addPatterns(obj,constraints,newPatterns)
|
||||||
|
# The master problem is solved, and the dual variables are returned
|
||||||
|
duals = masterSolve(prob)
|
||||||
|
# The sub problem is solved and a new pattern will be returned if there is one
|
||||||
|
# which can reduce the master objective function
|
||||||
|
newPatterns = subSolve(duals)
|
||||||
|
|
||||||
|
# The master problem is solved with Integer Constraints not relaxed
|
||||||
|
solution, varsdict = masterSolve(prob,relax = False)
|
||||||
|
|
||||||
|
# Display Solution
|
||||||
|
for i,j in varsdict.items():
|
||||||
|
print i, "=", j
|
||||||
|
|
||||||
|
print "objective = ", solution
|
||||||
111
examples/Sudoku1.py
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
"""
|
||||||
|
The Sudoku Problem Formulation for the PuLP Modeller
|
||||||
|
|
||||||
|
Authors: Antony Phillips, Dr Stuart Mitcehll
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Import PuLP modeler functions
|
||||||
|
from pulp import *
|
||||||
|
|
||||||
|
# A list of strings from "1" to "9" is created
|
||||||
|
Sequence = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
|
||||||
|
|
||||||
|
# The Vals, Rows and Cols sequences all follow this form
|
||||||
|
Vals = Sequence
|
||||||
|
Rows = Sequence
|
||||||
|
Cols = Sequence
|
||||||
|
|
||||||
|
# The boxes list is created, with the row and column index of each square in each box
|
||||||
|
Boxes =[]
|
||||||
|
for i in range(3):
|
||||||
|
for j in range(3):
|
||||||
|
Boxes += [[(Rows[3*i+k],Cols[3*j+l]) for k in range(3) for l in range(3)]]
|
||||||
|
|
||||||
|
# The prob variable is created to contain the problem data
|
||||||
|
prob = LpProblem("Sudoku Problem",LpMinimize)
|
||||||
|
|
||||||
|
# The problem variables are created
|
||||||
|
choices = LpVariable.dicts("Choice",(Vals,Rows,Cols),0,1,LpInteger)
|
||||||
|
|
||||||
|
# The arbitrary objective function is added
|
||||||
|
prob += 0, "Arbitrary Objective Function"
|
||||||
|
|
||||||
|
# A constraint ensuring that only one value can be in each square is created
|
||||||
|
for r in Rows:
|
||||||
|
for c in Cols:
|
||||||
|
prob += lpSum([choices[v][r][c] for v in Vals]) == 1, ""
|
||||||
|
|
||||||
|
# The row, column and box constraints are added for each value
|
||||||
|
for v in Vals:
|
||||||
|
for r in Rows:
|
||||||
|
prob += lpSum([choices[v][r][c] for c in Cols]) == 1,""
|
||||||
|
|
||||||
|
for c in Cols:
|
||||||
|
prob += lpSum([choices[v][r][c] for r in Rows]) == 1,""
|
||||||
|
|
||||||
|
for b in Boxes:
|
||||||
|
prob += lpSum([choices[v][r][c] for (r,c) in b]) == 1,""
|
||||||
|
|
||||||
|
# The starting numbers are entered as constraints
|
||||||
|
prob += choices["5"]["1"]["1"] == 1,""
|
||||||
|
prob += choices["6"]["2"]["1"] == 1,""
|
||||||
|
prob += choices["8"]["4"]["1"] == 1,""
|
||||||
|
prob += choices["4"]["5"]["1"] == 1,""
|
||||||
|
prob += choices["7"]["6"]["1"] == 1,""
|
||||||
|
prob += choices["3"]["1"]["2"] == 1,""
|
||||||
|
prob += choices["9"]["3"]["2"] == 1,""
|
||||||
|
prob += choices["6"]["7"]["2"] == 1,""
|
||||||
|
prob += choices["8"]["3"]["3"] == 1,""
|
||||||
|
prob += choices["1"]["2"]["4"] == 1,""
|
||||||
|
prob += choices["8"]["5"]["4"] == 1,""
|
||||||
|
prob += choices["4"]["8"]["4"] == 1,""
|
||||||
|
prob += choices["7"]["1"]["5"] == 1,""
|
||||||
|
prob += choices["9"]["2"]["5"] == 1,""
|
||||||
|
prob += choices["6"]["4"]["5"] == 1,""
|
||||||
|
prob += choices["2"]["6"]["5"] == 1,""
|
||||||
|
prob += choices["1"]["8"]["5"] == 1,""
|
||||||
|
prob += choices["8"]["9"]["5"] == 1,""
|
||||||
|
prob += choices["5"]["2"]["6"] == 1,""
|
||||||
|
prob += choices["3"]["5"]["6"] == 1,""
|
||||||
|
prob += choices["9"]["8"]["6"] == 1,""
|
||||||
|
prob += choices["2"]["7"]["7"] == 1,""
|
||||||
|
prob += choices["6"]["3"]["8"] == 1,""
|
||||||
|
prob += choices["8"]["7"]["8"] == 1,""
|
||||||
|
prob += choices["7"]["9"]["8"] == 1,""
|
||||||
|
prob += choices["3"]["4"]["9"] == 1,""
|
||||||
|
prob += choices["1"]["5"]["9"] == 1,""
|
||||||
|
prob += choices["6"]["6"]["9"] == 1,""
|
||||||
|
prob += choices["5"]["8"]["9"] == 1,""
|
||||||
|
|
||||||
|
# The problem data is written to an .lp file
|
||||||
|
prob.writeLP("Sudoku.lp")
|
||||||
|
|
||||||
|
# The problem is solved using PuLP's choice of Solver
|
||||||
|
prob.solve()
|
||||||
|
|
||||||
|
# The status of the solution is printed to the screen
|
||||||
|
print "Status:", LpStatus[prob.status]
|
||||||
|
|
||||||
|
# A file called sudokuout.txt is created/overwritten for writing to
|
||||||
|
sudokuout = open('sudokuout.txt','w')
|
||||||
|
|
||||||
|
# The solution is written to the sudokuout.txt file
|
||||||
|
for r in Rows:
|
||||||
|
if r == "1" or r == "4" or r == "7":
|
||||||
|
sudokuout.write("+-------+-------+-------+\n")
|
||||||
|
for c in Cols:
|
||||||
|
for v in Vals:
|
||||||
|
if value(choices[v][r][c])==1:
|
||||||
|
|
||||||
|
if c == "1" or c == "4" or c =="7":
|
||||||
|
sudokuout.write("| ")
|
||||||
|
|
||||||
|
sudokuout.write(v + " ")
|
||||||
|
|
||||||
|
if c == "9":
|
||||||
|
sudokuout.write("|\n")
|
||||||
|
sudokuout.write("+-------+-------+-------+")
|
||||||
|
sudokuout.close()
|
||||||
|
|
||||||
|
# The location of the solution is give to the user
|
||||||
|
print "Solution Written to sudokuout.txt"
|
||||||
115
examples/Sudoku2.py
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
"""
|
||||||
|
The Looping Sudoku Problem Formulation for the PuLP Modeller
|
||||||
|
|
||||||
|
Authors: Antony Phillips, Dr Stuart Mitcehll
|
||||||
|
"""
|
||||||
|
# Import PuLP modeler functions
|
||||||
|
from pulp import *
|
||||||
|
|
||||||
|
# A list of strings from "1" to "9" is created
|
||||||
|
Sequence = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
|
||||||
|
|
||||||
|
# The Vals, Rows and Cols sequences all follow this form
|
||||||
|
Vals = Sequence
|
||||||
|
Rows = Sequence
|
||||||
|
Cols = Sequence
|
||||||
|
|
||||||
|
# The boxes list is created, with the row and column index of each square in each box
|
||||||
|
Boxes =[]
|
||||||
|
for i in range(3):
|
||||||
|
for j in range(3):
|
||||||
|
Boxes += [[(Rows[3*i+k],Cols[3*j+l]) for k in range(3) for l in range(3)]]
|
||||||
|
|
||||||
|
# The prob variable is created to contain the problem data
|
||||||
|
prob = LpProblem("Sudoku Problem",LpMinimize)
|
||||||
|
|
||||||
|
# The problem variables are created
|
||||||
|
choices = LpVariable.dicts("Choice",(Vals,Rows,Cols),0,1,LpInteger)
|
||||||
|
|
||||||
|
# The arbitrary objective function is added
|
||||||
|
prob += 0, "Arbitrary Objective Function"
|
||||||
|
|
||||||
|
# A constraint ensuring that only one value can be in each square is created
|
||||||
|
for r in Rows:
|
||||||
|
for c in Cols:
|
||||||
|
prob += lpSum([choices[v][r][c] for v in Vals]) == 1, ""
|
||||||
|
|
||||||
|
# The row, column and box constraints are added for each value
|
||||||
|
for v in Vals:
|
||||||
|
for r in Rows:
|
||||||
|
prob += lpSum([choices[v][r][c] for c in Cols]) == 1,""
|
||||||
|
|
||||||
|
for c in Cols:
|
||||||
|
prob += lpSum([choices[v][r][c] for r in Rows]) == 1,""
|
||||||
|
|
||||||
|
for b in Boxes:
|
||||||
|
prob += lpSum([choices[v][r][c] for (r,c) in b]) == 1,""
|
||||||
|
|
||||||
|
# The starting numbers are entered as constraints
|
||||||
|
prob += choices["5"]["1"]["1"] == 1,""
|
||||||
|
prob += choices["6"]["2"]["1"] == 1,""
|
||||||
|
prob += choices["8"]["4"]["1"] == 1,""
|
||||||
|
prob += choices["4"]["5"]["1"] == 1,""
|
||||||
|
prob += choices["7"]["6"]["1"] == 1,""
|
||||||
|
prob += choices["3"]["1"]["2"] == 1,""
|
||||||
|
prob += choices["9"]["3"]["2"] == 1,""
|
||||||
|
prob += choices["6"]["7"]["2"] == 1,""
|
||||||
|
prob += choices["8"]["3"]["3"] == 1,""
|
||||||
|
prob += choices["1"]["2"]["4"] == 1,""
|
||||||
|
prob += choices["8"]["5"]["4"] == 1,""
|
||||||
|
prob += choices["4"]["8"]["4"] == 1,""
|
||||||
|
prob += choices["7"]["1"]["5"] == 1,""
|
||||||
|
prob += choices["9"]["2"]["5"] == 1,""
|
||||||
|
prob += choices["6"]["4"]["5"] == 1,""
|
||||||
|
prob += choices["2"]["6"]["5"] == 1,""
|
||||||
|
prob += choices["1"]["8"]["5"] == 1,""
|
||||||
|
prob += choices["8"]["9"]["5"] == 1,""
|
||||||
|
prob += choices["5"]["2"]["6"] == 1,""
|
||||||
|
prob += choices["3"]["5"]["6"] == 1,""
|
||||||
|
prob += choices["9"]["8"]["6"] == 1,""
|
||||||
|
prob += choices["2"]["7"]["7"] == 1,""
|
||||||
|
prob += choices["6"]["3"]["8"] == 1,""
|
||||||
|
prob += choices["8"]["7"]["8"] == 1,""
|
||||||
|
prob += choices["7"]["9"]["8"] == 1,""
|
||||||
|
prob += choices["3"]["4"]["9"] == 1,""
|
||||||
|
prob += choices["1"]["5"]["9"] == 1,""
|
||||||
|
prob += choices["6"]["6"]["9"] == 1,""
|
||||||
|
prob += choices["5"]["8"]["9"] == 1,""
|
||||||
|
|
||||||
|
# The problem data is written to an .lp file
|
||||||
|
prob.writeLP("Sudoku.lp")
|
||||||
|
|
||||||
|
# A file called sudokuout.txt is created/overwritten for writing to
|
||||||
|
sudokuout = open('sudokuout.txt','w')
|
||||||
|
|
||||||
|
while True:
|
||||||
|
prob.solve()
|
||||||
|
# The status of the solution is printed to the screen
|
||||||
|
print "Status:", LpStatus[prob.status]
|
||||||
|
# The solution is printed if it was deemed "optimal" i.e met the constraints
|
||||||
|
if LpStatus[prob.status] == "Optimal":
|
||||||
|
# The solution is written to the sudokuout.txt file
|
||||||
|
for r in Rows:
|
||||||
|
if r == "1" or r == "4" or r == "7":
|
||||||
|
sudokuout.write("+-------+-------+-------+\n")
|
||||||
|
for c in Cols:
|
||||||
|
for v in Vals:
|
||||||
|
if value(choices[v][r][c])==1:
|
||||||
|
if c == "1" or c == "4" or c =="7":
|
||||||
|
sudokuout.write("| ")
|
||||||
|
sudokuout.write(v + " ")
|
||||||
|
if c == "9":
|
||||||
|
sudokuout.write("|\n")
|
||||||
|
sudokuout.write("+-------+-------+-------+\n\n")
|
||||||
|
# The constraint is added that the same solution cannot be returned again
|
||||||
|
prob += lpSum([choices[v][r][c] for v in Vals
|
||||||
|
for r in Rows
|
||||||
|
for c in Cols
|
||||||
|
if value(choices[v][r][c])==1]) <= 80
|
||||||
|
# If a new optimal solution cannot be found, we end the program
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
sudokuout.close()
|
||||||
|
|
||||||
|
# The location of the solutions is give to the user
|
||||||
|
print "Solutions Written to sudokuout.txt"
|
||||||
41
examples/WhiskasModel1.py
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
"""
|
||||||
|
The Simplified Whiskas Model Python Formulation for the PuLP Modeller
|
||||||
|
|
||||||
|
Authors: Antony Phillips, Dr Stuart Mitchell 2007
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Import PuLP modeler functions
|
||||||
|
from pulp import *
|
||||||
|
|
||||||
|
# Create the 'prob' variable to contain the problem data
|
||||||
|
prob = LpProblem("The Whiskas Problem",LpMinimize)
|
||||||
|
|
||||||
|
# The 2 variables Beef and Chicken are created with a lower limit of zero
|
||||||
|
x1=LpVariable("ChickenPercent",0,None,LpInteger)
|
||||||
|
x2=LpVariable("BeefPercent",0)
|
||||||
|
|
||||||
|
# The objective function is added to 'prob' first
|
||||||
|
prob += 0.013*x1 + 0.008*x2, "Total Cost of Ingredients per can"
|
||||||
|
|
||||||
|
# The five constraints are entered
|
||||||
|
prob += x1 + x2 == 100, "PercentagesSum"
|
||||||
|
prob += 0.100*x1 + 0.200*x2 >= 8.0, "ProteinRequirement"
|
||||||
|
prob += 0.080*x1 + 0.100*x2 >= 6.0, "FatRequirement"
|
||||||
|
prob += 0.001*x1 + 0.005*x2 <= 2.0, "FibreRequirement"
|
||||||
|
prob += 0.002*x1 + 0.005*x2 <= 0.4, "SaltRequirement"
|
||||||
|
|
||||||
|
# The problem data is written to an .lp file
|
||||||
|
prob.writeLP("WhiskasModel.lp")
|
||||||
|
|
||||||
|
# The problem is solved using PuLP's choice of Solver
|
||||||
|
prob.solve()
|
||||||
|
|
||||||
|
# The status of the solution is printed to the screen
|
||||||
|
print "Status:", LpStatus[prob.status]
|
||||||
|
|
||||||
|
# Each of the variables is printed with it's resolved optimum value
|
||||||
|
for v in prob.variables():
|
||||||
|
print v.name, "=", v.varValue
|
||||||
|
|
||||||
|
# The optimised objective function value is printed to the screen
|
||||||
|
print "Total Cost of Ingredients per can = ", value(prob.objective)
|
||||||
83
examples/WhiskasModel2.py
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
"""
|
||||||
|
The Full Whiskas Model Python Formulation for the PuLP Modeller
|
||||||
|
|
||||||
|
Authors: Antony Phillips, Dr Stuart Mitchell 2007
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Import PuLP modeler functions
|
||||||
|
from pulp import *
|
||||||
|
|
||||||
|
# Creates a list of the Ingredients
|
||||||
|
Ingredients = ['CHICKEN', 'BEEF', 'MUTTON', 'RICE', 'WHEAT', 'GEL']
|
||||||
|
|
||||||
|
# A dictionary of the costs of each of the Ingredients is created
|
||||||
|
costs = {'CHICKEN': 0.013,
|
||||||
|
'BEEF': 0.008,
|
||||||
|
'MUTTON': 0.010,
|
||||||
|
'RICE': 0.002,
|
||||||
|
'WHEAT': 0.005,
|
||||||
|
'GEL': 0.001}
|
||||||
|
|
||||||
|
# A dictionary of the protein percent in each of the Ingredients is created
|
||||||
|
proteinPercent = {'CHICKEN': 0.100,
|
||||||
|
'BEEF': 0.200,
|
||||||
|
'MUTTON': 0.150,
|
||||||
|
'RICE': 0.000,
|
||||||
|
'WHEAT': 0.040,
|
||||||
|
'GEL': 0.000}
|
||||||
|
|
||||||
|
# A dictionary of the fat percent in each of the Ingredients is created
|
||||||
|
fatPercent = {'CHICKEN': 0.080,
|
||||||
|
'BEEF': 0.100,
|
||||||
|
'MUTTON': 0.110,
|
||||||
|
'RICE': 0.010,
|
||||||
|
'WHEAT': 0.010,
|
||||||
|
'GEL': 0.000}
|
||||||
|
|
||||||
|
# A dictionary of the fibre percent in each of the Ingredients is created
|
||||||
|
fibrePercent = {'CHICKEN': 0.001,
|
||||||
|
'BEEF': 0.005,
|
||||||
|
'MUTTON': 0.003,
|
||||||
|
'RICE': 0.100,
|
||||||
|
'WHEAT': 0.150,
|
||||||
|
'GEL': 0.000}
|
||||||
|
|
||||||
|
# A dictionary of the salt percent in each of the Ingredients is created
|
||||||
|
saltPercent = {'CHICKEN': 0.002,
|
||||||
|
'BEEF': 0.005,
|
||||||
|
'MUTTON': 0.007,
|
||||||
|
'RICE': 0.002,
|
||||||
|
'WHEAT': 0.008,
|
||||||
|
'GEL': 0.000}
|
||||||
|
|
||||||
|
# Create the 'prob' variable to contain the problem data
|
||||||
|
prob = LpProblem("The Whiskas Problem", LpMinimize)
|
||||||
|
|
||||||
|
# A dictionary called 'ingredient_vars' is created to contain the referenced Variables
|
||||||
|
ingredient_vars = LpVariable.dicts("Ingr",Ingredients,0)
|
||||||
|
|
||||||
|
# The objective function is added to 'prob' first
|
||||||
|
prob += lpSum([costs[i]*ingredient_vars[i] for i in Ingredients]), "Total Cost of Ingredients per can"
|
||||||
|
|
||||||
|
# The five constraints are added to 'prob'
|
||||||
|
prob += lpSum([ingredient_vars[i] for i in Ingredients]) == 100, "PercentagesSum"
|
||||||
|
prob += lpSum([proteinPercent[i] * ingredient_vars[i] for i in Ingredients]) >= 8.0, "ProteinRequirement"
|
||||||
|
prob += lpSum([fatPercent[i] * ingredient_vars[i] for i in Ingredients]) >= 6.0, "FatRequirement"
|
||||||
|
prob += lpSum([fibrePercent[i] * ingredient_vars[i] for i in Ingredients]) <= 2.0, "FibreRequirement"
|
||||||
|
prob += lpSum([saltPercent[i] * ingredient_vars[i] for i in Ingredients]) <= 0.4, "SaltRequirement"
|
||||||
|
|
||||||
|
# The problem data is written to an .lp file
|
||||||
|
prob.writeLP("WhiskasModel2.lp")
|
||||||
|
|
||||||
|
# The problem is solved using PuLP's choice of Solver
|
||||||
|
prob.solve()
|
||||||
|
|
||||||
|
# The status of the solution is printed to the screen
|
||||||
|
print "Status:", LpStatus[prob.status]
|
||||||
|
|
||||||
|
# Each of the variables is printed with it's resolved optimum value
|
||||||
|
for v in prob.variables():
|
||||||
|
print v.name, "=", v.varValue
|
||||||
|
|
||||||
|
# The optimised objective function value is printed to the screen
|
||||||
|
print "Total Cost of Ingredients per can = ", value(prob.objective)
|
||||||
31
examples/furniture.py
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
"""
|
||||||
|
The Furniture problem from EngSci391 for the PuLP Modeller
|
||||||
|
Author: Dr Stuart Mitchell 2007
|
||||||
|
"""
|
||||||
|
from pulp import *
|
||||||
|
Chairs = ["A","B"]
|
||||||
|
costs = {"A":100,
|
||||||
|
"B":150}
|
||||||
|
Resources = ["Lathe","Polisher"]
|
||||||
|
capacity = {"Lathe" : 40,
|
||||||
|
"Polisher" : 48}
|
||||||
|
activity = [ #Chairs
|
||||||
|
#A B
|
||||||
|
[1, 2], #Lathe
|
||||||
|
[3, 1.5] #Polisher
|
||||||
|
]
|
||||||
|
activity = makeDict([Resources,Chairs],activity)
|
||||||
|
prob = LpProblem("Furniture Manufacturing Problem", LpMaximize)
|
||||||
|
vars = LpVariable.dicts("Number of Chairs",Chairs, lowBound = 0)
|
||||||
|
#objective
|
||||||
|
prob += lpSum([costs[c]*vars[c] for c in Chairs])
|
||||||
|
for r in Resources:
|
||||||
|
prob += lpSum([activity[r][c]*vars[c] for c in Chairs]) <= capacity[r], \
|
||||||
|
"capacity_of_%s"%r
|
||||||
|
prob.writeLP("furniture.lp")
|
||||||
|
prob.solve()
|
||||||
|
# Each of the variables is printed with it's value
|
||||||
|
for v in prob.variables():
|
||||||
|
print v.name, "=", v.varValue
|
||||||
|
# The optimised objective function value is printed to the screen
|
||||||
|
print "Total Revenue from Production = ", value(prob.objective)
|
||||||
52
examples/test1.py
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# @(#) $Jeannot: test1.py,v 1.11 2005/01/06 21:22:39 js Exp $
|
||||||
|
|
||||||
|
# Import PuLP modeler functions
|
||||||
|
from pulp import *
|
||||||
|
|
||||||
|
# A new LP problem
|
||||||
|
prob = LpProblem("test1", LpMinimize)
|
||||||
|
|
||||||
|
# Variables
|
||||||
|
# 0 <= x <= 4
|
||||||
|
x = LpVariable("x", 0, 4)
|
||||||
|
# -1 <= y <= 1
|
||||||
|
y = LpVariable("y", -1, 1)
|
||||||
|
# 0 <= z
|
||||||
|
z = LpVariable("z", 0)
|
||||||
|
# Use None for +/- Infinity, i.e. z <= 0 -> LpVariable("z", None, 0)
|
||||||
|
|
||||||
|
# Objective
|
||||||
|
prob += x + 4*y + 9*z, "obj"
|
||||||
|
# (the name at the end is facultative)
|
||||||
|
|
||||||
|
# Constraints
|
||||||
|
prob += x+y <= 5, "c1"
|
||||||
|
prob += x+z >= 10, "c2"
|
||||||
|
prob += -y+z == 7, "c3"
|
||||||
|
# (the names at the end are facultative)
|
||||||
|
|
||||||
|
# Write the problem as an LP file
|
||||||
|
prob.writeLP("test1.lp")
|
||||||
|
|
||||||
|
# Solve the problem using the default solver
|
||||||
|
prob.solve()
|
||||||
|
# Use prob.solve(GLPK()) instead to choose GLPK as the solver
|
||||||
|
# Use GLPK(msg = 0) to suppress GLPK messages
|
||||||
|
# If GLPK is not in your path and you lack the pulpGLPK module,
|
||||||
|
# replace GLPK() with GLPK("/path/")
|
||||||
|
# Where /path/ is the path to glpsol (excluding glpsol itself).
|
||||||
|
# If you want to use CPLEX, use CPLEX() instead of GLPK().
|
||||||
|
# If you want to use XPRESS, use XPRESS() instead of GLPK().
|
||||||
|
# If you want to use COIN, use COIN() instead of GLPK(). In this last case,
|
||||||
|
# two paths may be provided (one to clp, one to cbc).
|
||||||
|
|
||||||
|
# Print the status of the solved LP
|
||||||
|
print "Status:", LpStatus[prob.status]
|
||||||
|
|
||||||
|
# Print the value of the variables at the optimum
|
||||||
|
for v in prob.variables():
|
||||||
|
print v.name, "=", v.varValue
|
||||||
|
|
||||||
|
# Print the value of the objective
|
||||||
|
print "objective=", value(prob.objective)
|
||||||
48
examples/test2.py
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# @(#) $Jeannot: test2.py,v 1.16 2004/03/20 17:06:54 js Exp $
|
||||||
|
|
||||||
|
# This example is a PuLP rendition of the todd.mod problem included in the GLPK
|
||||||
|
# 4.4 distribution. It's a hard knapsack problem.
|
||||||
|
|
||||||
|
# Import PuLP modeler functions
|
||||||
|
from pulp import *
|
||||||
|
|
||||||
|
# Import math functions
|
||||||
|
from math import *
|
||||||
|
|
||||||
|
# A new LP problem
|
||||||
|
prob = LpProblem("test2", LpMaximize)
|
||||||
|
|
||||||
|
# Parameters
|
||||||
|
# Size of the problem
|
||||||
|
n = 15
|
||||||
|
k = floor(log(n)/log(2));
|
||||||
|
|
||||||
|
# A vector of n binary variables
|
||||||
|
x = LpVariable.matrix("x", range(n), 0, 1, LpInteger)
|
||||||
|
|
||||||
|
# A vector of weights
|
||||||
|
a = [pow(2,k + n + 1) + pow(2,k + n + 1 - j) + 1 for j in range(1,n+1)]
|
||||||
|
# The maximum weight
|
||||||
|
b = 0.5 * floor(sum(a))
|
||||||
|
|
||||||
|
# The total weight
|
||||||
|
weight = lpDot(a, x)
|
||||||
|
|
||||||
|
# Objective
|
||||||
|
prob += weight
|
||||||
|
# Constraint
|
||||||
|
prob += weight <= b
|
||||||
|
|
||||||
|
# Resolution
|
||||||
|
prob.solve()
|
||||||
|
|
||||||
|
# Print the status of the solved LP
|
||||||
|
print "Status:", LpStatus[prob.status]
|
||||||
|
|
||||||
|
# Print the value of the variables at the optimum
|
||||||
|
for v in prob.variables():
|
||||||
|
print v.name, "=", v.varValue
|
||||||
|
|
||||||
|
# Print the value of the objective
|
||||||
|
print "objective=", value(prob.objective)
|
||||||
122
examples/test3.py
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# @(#) $Jeannot: test3.py,v 1.3 2004/03/20 17:06:54 js Exp $
|
||||||
|
|
||||||
|
# Deterministic generation planning using mixed integer linear programming.
|
||||||
|
|
||||||
|
# The goal is to minimise the cost of generation while satisfaying demand
|
||||||
|
# using a few thermal units and an hydro unit.
|
||||||
|
# The thermal units have a proportional cost and a startup cost.
|
||||||
|
# The hydro unit has an initial storage.
|
||||||
|
|
||||||
|
from pulp import *
|
||||||
|
from math import *
|
||||||
|
|
||||||
|
prob = LpProblem("test3", LpMinimize)
|
||||||
|
|
||||||
|
# The number of time steps
|
||||||
|
tmax = 9
|
||||||
|
# The number of thermal units
|
||||||
|
units = 5
|
||||||
|
# The minimum demand
|
||||||
|
dmin = 10.0
|
||||||
|
# The maximum demand
|
||||||
|
dmax = 150.0
|
||||||
|
# The maximum thermal production
|
||||||
|
tpmax = 150.0
|
||||||
|
# The maximum hydro production
|
||||||
|
hpmax = 100.0
|
||||||
|
# Initial hydro storage
|
||||||
|
sini = 50.0
|
||||||
|
|
||||||
|
# Time range
|
||||||
|
time = range(tmax)
|
||||||
|
# Time range (and one more step for the last state of plants)
|
||||||
|
xtime = range(tmax+1)
|
||||||
|
# Units range
|
||||||
|
unit = range(units)
|
||||||
|
# The demand
|
||||||
|
demand = [dmin+(dmax-dmin)*0.5 + 0.5*(dmax-dmin)*sin(4*t*2*3.1415/tmax) for t in time]
|
||||||
|
# Maximum output for the thermal units
|
||||||
|
pmax = [tpmax / units for i in unit]
|
||||||
|
# Minimum output for the thermal units
|
||||||
|
pmin = [tpmax / (units*3.0) for i in unit]
|
||||||
|
# Proportional cost of the thermal units
|
||||||
|
costs = [i+1 for i in unit]
|
||||||
|
# Startup cost of the thermal units.
|
||||||
|
startupcosts = [100*(i+1) for i in unit]
|
||||||
|
|
||||||
|
# Production variables for each time step and each thermal unit.
|
||||||
|
p = LpVariable.matrix("p", (time, unit), 0)
|
||||||
|
for t in time:
|
||||||
|
for i in unit:
|
||||||
|
p[t][i].upBound = pmax[i]
|
||||||
|
|
||||||
|
# State (started/stopped) variables for each time step and each thermal unit
|
||||||
|
d = LpVariable.matrix("d", (xtime, unit), 0, 1, LpInteger)
|
||||||
|
|
||||||
|
# Production constraint relative to the unit state (started/stoped)
|
||||||
|
for t in time:
|
||||||
|
for i in unit:
|
||||||
|
# If the unit is not started (d==0) then p<=0 else p<=pmax
|
||||||
|
prob += p[t][i] <= pmax[i]*d[t][i]
|
||||||
|
# If the unit is not started then p>=0 else p>= pmin
|
||||||
|
prob += p[t][i] >= pmin[i]*d[t][i]
|
||||||
|
|
||||||
|
# Startup variables: 1 if the unit will be started next time step
|
||||||
|
u = LpVariable.matrix("u", (time, unit), 0)
|
||||||
|
|
||||||
|
# Dynamic startup constraints
|
||||||
|
# Initialy, all groups are started
|
||||||
|
for t in time:
|
||||||
|
for i in unit:
|
||||||
|
# u>=1 if the unit is started next time step
|
||||||
|
prob += u[t][i] >= d[t+1][i] - d[t][i]
|
||||||
|
|
||||||
|
# Storage for the hydro plant (must not go below 0)
|
||||||
|
s = LpVariable.matrix("s", xtime, 0)
|
||||||
|
|
||||||
|
# Initial storage
|
||||||
|
s[0] = sini
|
||||||
|
|
||||||
|
# Hydro production
|
||||||
|
ph = [s[t]-s[t+1] for t in time]
|
||||||
|
for t in time:
|
||||||
|
# Must be positive (no pumping)
|
||||||
|
prob += ph[t] >= 0
|
||||||
|
# And lower than hpmax
|
||||||
|
prob += ph[t] <= hpmax
|
||||||
|
|
||||||
|
# Total production must equal demand
|
||||||
|
for t in time:
|
||||||
|
prob += demand[t] == lpSum(p[t]) + ph[t]
|
||||||
|
|
||||||
|
# Thermal production cost
|
||||||
|
ctp = lpSum([lpSum([p[t][i] for t in time])*costs[i] for i in unit])
|
||||||
|
# Startup costs
|
||||||
|
cts = lpSum([lpSum([u[t][i] for t in time])*startupcosts[i] for i in unit])
|
||||||
|
# The objective is the total cost
|
||||||
|
prob += ctp + cts
|
||||||
|
|
||||||
|
# Solve the problem
|
||||||
|
prob.solve()
|
||||||
|
|
||||||
|
print "Minimum total cost:", prob.objective.value()
|
||||||
|
|
||||||
|
# Print the results
|
||||||
|
print " D S U ",
|
||||||
|
for i in unit: print " T%d " %i,
|
||||||
|
print
|
||||||
|
|
||||||
|
for t in time:
|
||||||
|
# Demand, hydro storage, hydro production
|
||||||
|
print "%5.1f" % demand[t], "%5.1f" % value(s[t]), "%5.1f" % value(ph[t]),
|
||||||
|
for i in unit:
|
||||||
|
# Thermal production
|
||||||
|
print "%4.1f" % value(p[t][i]),
|
||||||
|
# The state of the unit
|
||||||
|
if value(d[t][i]): print "+",
|
||||||
|
else: print "-",
|
||||||
|
# Wether the unit will be started
|
||||||
|
if value(u[t][i]): print "*",
|
||||||
|
else: print " ",
|
||||||
|
print
|
||||||
59
examples/test4.py
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# @(#) $Jeannot: test4.py,v 1.5 2004/03/20 17:06:54 js Exp $
|
||||||
|
|
||||||
|
# A two stage stochastic planification problem
|
||||||
|
|
||||||
|
# Example taken from:
|
||||||
|
# "On Optimal Allocation of Indivisibles under Incertainty"
|
||||||
|
# Vladimir I. Norkin, Yuri M. Ermoliev, Andrzej Ruszczynski
|
||||||
|
# IIASA, WP-94-021, April 1994 (revised October 1995).
|
||||||
|
|
||||||
|
from pulp import *
|
||||||
|
from random import *
|
||||||
|
|
||||||
|
C = 50
|
||||||
|
B = 500 # Resources available for the two years
|
||||||
|
s = 20 # Number of scenarios
|
||||||
|
n = 10 # Number of projects
|
||||||
|
|
||||||
|
N = range(n)
|
||||||
|
S = range(s)
|
||||||
|
|
||||||
|
# First year costs
|
||||||
|
c = [randint(0,C) for i in N]
|
||||||
|
# First year resources
|
||||||
|
d = [randint(0,C) for i in N]
|
||||||
|
# a=debut, b=taille
|
||||||
|
interval = [[(randint(0,C), randint(0,C)) for i in N] for j in S]
|
||||||
|
# Final earnings
|
||||||
|
q = [[randint(ai, ai+bi) for ai,bi in ab] for ab in interval]
|
||||||
|
# Second year resources
|
||||||
|
delta = [[randint(ai, ai+bi) for ai,bi in ab] for ab in interval]
|
||||||
|
|
||||||
|
# Variables
|
||||||
|
# x : Whether or not to start a project
|
||||||
|
x = LpVariable.matrix("x", (N,), 0, 1, LpInteger)
|
||||||
|
# y : Whether or not to finish it, in each scenario
|
||||||
|
y = LpVariable.matrix("y", (S, N), 0, 1, LpInteger)
|
||||||
|
|
||||||
|
# Problem
|
||||||
|
lp = LpProblem("Planification", LpMinimize)
|
||||||
|
|
||||||
|
# Objective: expected earnings
|
||||||
|
lp += lpDot(x, c) - lpDot(q, y)/float(s)
|
||||||
|
|
||||||
|
# Resources constraints for each scenario
|
||||||
|
for j in S:
|
||||||
|
lp += lpDot(d, x) + lpDot(delta[j], y[j]) <= B
|
||||||
|
|
||||||
|
# We can only finish a project that was started
|
||||||
|
for i in N:
|
||||||
|
for j in S:
|
||||||
|
lp += y[j][i] <= x[i]
|
||||||
|
|
||||||
|
# Resolution
|
||||||
|
lp.solve()
|
||||||
|
|
||||||
|
# Solution printing
|
||||||
|
for i in N:
|
||||||
|
print x[i], "=", x[i].value()
|
||||||
53
examples/test5.py
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# @(#) $Jeannot: test5.py,v 1.2 2004/03/20 17:06:54 js Exp $
|
||||||
|
|
||||||
|
# Market splitting problems from:
|
||||||
|
# G. Cornuejols, M. Dawande, A class of hard small 0-1 programs, 1998.
|
||||||
|
|
||||||
|
# With m>=4, these problems are often *very* difficult.
|
||||||
|
|
||||||
|
# Import PuLP modeler functions
|
||||||
|
from pulp import *
|
||||||
|
|
||||||
|
# Import random number generation functions
|
||||||
|
from random import *
|
||||||
|
|
||||||
|
# A new LP problem
|
||||||
|
prob = LpProblem("test5", LpMinimize)
|
||||||
|
|
||||||
|
# Parameters
|
||||||
|
# Number of constraints
|
||||||
|
m = 3
|
||||||
|
# Size of the integers involved
|
||||||
|
D = 100
|
||||||
|
|
||||||
|
# Number of variables
|
||||||
|
n = 10*(m-1)
|
||||||
|
|
||||||
|
# A vector of n binary variables
|
||||||
|
x = LpVariable.matrix("x", range(n), 0, 1, LpInteger)
|
||||||
|
|
||||||
|
# Slacks
|
||||||
|
s = LpVariable.matrix("s", range(m), 0)
|
||||||
|
w = LpVariable.matrix("w", range(m), 0)
|
||||||
|
|
||||||
|
# Objective
|
||||||
|
prob += lpSum(s) + lpSum(w)
|
||||||
|
|
||||||
|
# Constraints
|
||||||
|
d = [[randint(0,D) for i in range(n)] for j in range(m)]
|
||||||
|
for j in range(m):
|
||||||
|
prob += lpDot(d[j], x) + s[j] - w[j] == lpSum(d[j])/2
|
||||||
|
|
||||||
|
# Resolution
|
||||||
|
prob.solve()
|
||||||
|
|
||||||
|
# Print the status of the solved LP
|
||||||
|
print "Status:", LpStatus[prob.status]
|
||||||
|
|
||||||
|
# Print the value of the variables at the optimum
|
||||||
|
for v in prob.variables():
|
||||||
|
print v.name, "=", v.varValue
|
||||||
|
|
||||||
|
# Print the value of the objective
|
||||||
|
print "objective=", value(prob.objective)
|
||||||
61
examples/test6.py
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# @(#) $Jeannot: test1.py,v 1.11 2005/01/06 21:22:39 js Exp $
|
||||||
|
# Copywrite 2007 Stuart Mitchell
|
||||||
|
# Columnwise modelling
|
||||||
|
|
||||||
|
# Import PuLP modeler functions
|
||||||
|
from pulp import *
|
||||||
|
|
||||||
|
# A new LP problem
|
||||||
|
prob = LpProblem("test6", LpMinimize)
|
||||||
|
|
||||||
|
# objective
|
||||||
|
obj = LpConstraintVar("obj")
|
||||||
|
|
||||||
|
# constraints
|
||||||
|
|
||||||
|
a = LpConstraintVar("Ca", LpConstraintLE, 5)
|
||||||
|
|
||||||
|
b = LpConstraintVar("Cb", LpConstraintGE, 10)
|
||||||
|
|
||||||
|
c = LpConstraintVar("Cc", LpConstraintEQ, 7)
|
||||||
|
|
||||||
|
prob.setObjective(obj)
|
||||||
|
prob += a
|
||||||
|
prob += b
|
||||||
|
prob += c
|
||||||
|
|
||||||
|
# Variables
|
||||||
|
# 0 <= x <= 4
|
||||||
|
x = LpVariable("x", 0, 4, LpContinuous, obj + a + b)
|
||||||
|
# -1 <= y <= 1
|
||||||
|
y = LpVariable("y", -1, 1, LpContinuous, 4*obj + a - c)
|
||||||
|
# 0 <= z
|
||||||
|
z = LpVariable("z", 0, None, LpContinuous, 9*obj + b + c)
|
||||||
|
# Use None for +/- Infinity, i.e. z <= 0 -> LpVariable("z", None, 0)
|
||||||
|
|
||||||
|
|
||||||
|
# Write the problem as an LP file
|
||||||
|
prob.writeLP("test6.lp")
|
||||||
|
|
||||||
|
# Solve the problem using the default solver
|
||||||
|
prob.solve()
|
||||||
|
# Use prob.solve(GLPK()) instead to choose GLPK as the solver
|
||||||
|
# Use GLPK(msg = 0) to suppress GLPK messages
|
||||||
|
# If GLPK is not in your path and you lack the pulpGLPK module,
|
||||||
|
# replace GLPK() with GLPK("/path/")
|
||||||
|
# Where /path/ is the path to glpsol (excluding glpsol itself).
|
||||||
|
# If you want to use CPLEX, use CPLEX() instead of GLPK().
|
||||||
|
# If you want to use XPRESS, use XPRESS() instead of GLPK().
|
||||||
|
# If you want to use COIN, use COIN() instead of GLPK(). In this last case,
|
||||||
|
# two paths may be provided (one to clp, one to cbc).
|
||||||
|
|
||||||
|
# Print the status of the solved LP
|
||||||
|
print "Status:", LpStatus[prob.status]
|
||||||
|
|
||||||
|
# Print the value of the variables at the optimum
|
||||||
|
for v in prob.variables():
|
||||||
|
print v.name, "=", v.varValue
|
||||||
|
|
||||||
|
# Print the value of the objective
|
||||||
|
print "objective=", value(prob.objective)
|
||||||
35
examples/test7.py
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# Test for output of dual variables
|
||||||
|
|
||||||
|
# Import PuLP modeler functions
|
||||||
|
from pulp import *
|
||||||
|
|
||||||
|
# A new LP problem
|
||||||
|
prob = LpProblem("test7", LpMinimize)
|
||||||
|
|
||||||
|
x = LpVariable("x", 0, 4)
|
||||||
|
|
||||||
|
y = LpVariable("y", -1, 1)
|
||||||
|
|
||||||
|
z = LpVariable("z", 0)
|
||||||
|
|
||||||
|
prob += x + 4*y + 9*z, "obj"
|
||||||
|
|
||||||
|
prob += x + y <= 5, "c1"
|
||||||
|
prob += x + z >= 10,"c2"
|
||||||
|
prob += -y+ z == 7,"c3"
|
||||||
|
|
||||||
|
prob.writeLP("test7.lp")
|
||||||
|
|
||||||
|
prob.solve()
|
||||||
|
|
||||||
|
print "Status:", LpStatus[prob.status]
|
||||||
|
|
||||||
|
for v in prob.variables():
|
||||||
|
print v.name, "=", v.varValue, "\tReduced Cost =", v.dj
|
||||||
|
|
||||||
|
print "objective=", value(prob.objective)
|
||||||
|
|
||||||
|
print "\nSensitivity Analysis\nConstraint\t\tShadow Price\tSlack"
|
||||||
|
for name, c in prob.constraints.items():
|
||||||
|
print name, ":", c, "\t", c.pi, "\t\t", c.slack
|
||||||
50
examples/wedding.py
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
"""
|
||||||
|
A set partitioning model of a wedding seating problem
|
||||||
|
|
||||||
|
Authors: Stuart Mitchell 2009
|
||||||
|
"""
|
||||||
|
|
||||||
|
import pulp
|
||||||
|
|
||||||
|
max_tables = 5
|
||||||
|
max_table_size = 4
|
||||||
|
guests = 'A B C D E F G I J K L M N O P Q R'.split()
|
||||||
|
|
||||||
|
def happiness(table):
|
||||||
|
"""
|
||||||
|
Find the happiness of the table
|
||||||
|
- by calculating the maximum distance between the letters
|
||||||
|
"""
|
||||||
|
return abs(ord(table[0]) - ord(table[-1]))
|
||||||
|
|
||||||
|
#create list of all possible tables
|
||||||
|
possible_tables = [tuple(c) for c in pulp.allcombinations(guests,
|
||||||
|
max_table_size)]
|
||||||
|
|
||||||
|
#create a binary variable to state that a table setting is used
|
||||||
|
x = pulp.LpVariable.dicts('table', possible_tables,
|
||||||
|
lowBound = 0,
|
||||||
|
upBound = 1,
|
||||||
|
cat = pulp.LpInteger)
|
||||||
|
|
||||||
|
seating_model = pulp.LpProblem("Wedding Seating Model", pulp.LpMinimize)
|
||||||
|
|
||||||
|
seating_model += sum([happiness(table) * x[table] for table in possible_tables])
|
||||||
|
|
||||||
|
#specify the maximum number of tables
|
||||||
|
seating_model += sum([x[table] for table in possible_tables]) <= max_tables, \
|
||||||
|
"Maximum_number_of_tables"
|
||||||
|
|
||||||
|
#A guest must seated at one and only one table
|
||||||
|
for guest in guests:
|
||||||
|
seating_model += sum([x[table] for table in possible_tables
|
||||||
|
if guest in table]) == 1, "Must_seat_%s"%guest
|
||||||
|
|
||||||
|
seating_model.solve()
|
||||||
|
|
||||||
|
print "The choosen tables are out of a total of %s:"%len(possible_tables)
|
||||||
|
for table in possible_tables:
|
||||||
|
if x[table].value() == 1.0:
|
||||||
|
print table
|
||||||
|
|
||||||
|
|
||||||
278
ez_setup.py
Normal file
@@ -0,0 +1,278 @@
|
|||||||
|
#!python
|
||||||
|
"""Bootstrap setuptools installation
|
||||||
|
|
||||||
|
If you want to use setuptools in your package's setup.py, just include this
|
||||||
|
file in the same directory with it, and add this to the top of your setup.py::
|
||||||
|
|
||||||
|
from ez_setup import use_setuptools
|
||||||
|
use_setuptools()
|
||||||
|
|
||||||
|
If you want to require a specific version of setuptools, set a download
|
||||||
|
mirror, or use an alternate download directory, you can do so by supplying
|
||||||
|
the appropriate options to ``use_setuptools()``.
|
||||||
|
|
||||||
|
This file can also be run as a script to install or upgrade setuptools.
|
||||||
|
"""
|
||||||
|
import sys
|
||||||
|
DEFAULT_VERSION = "0.6c11"
|
||||||
|
DEFAULT_URL = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3]
|
||||||
|
|
||||||
|
md5_data = {
|
||||||
|
'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca',
|
||||||
|
'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb',
|
||||||
|
'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b',
|
||||||
|
'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a',
|
||||||
|
'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618',
|
||||||
|
'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac',
|
||||||
|
'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5',
|
||||||
|
'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4',
|
||||||
|
'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c',
|
||||||
|
'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b',
|
||||||
|
'setuptools-0.6c10-py2.3.egg': 'ce1e2ab5d3a0256456d9fc13800a7090',
|
||||||
|
'setuptools-0.6c10-py2.4.egg': '57d6d9d6e9b80772c59a53a8433a5dd4',
|
||||||
|
'setuptools-0.6c10-py2.5.egg': 'de46ac8b1c97c895572e5e8596aeb8c7',
|
||||||
|
'setuptools-0.6c10-py2.6.egg': '58ea40aef06da02ce641495523a0b7f5',
|
||||||
|
'setuptools-0.6c11-py2.3.egg': '2baeac6e13d414a9d28e7ba5b5a596de',
|
||||||
|
'setuptools-0.6c11-py2.4.egg': 'bd639f9b0eac4c42497034dec2ec0c2b',
|
||||||
|
'setuptools-0.6c11-py2.5.egg': '64c94f3bf7a72a13ec83e0b24f2749b2',
|
||||||
|
'setuptools-0.6c11-py2.6.egg': 'bfa92100bd772d5a213eedd356d64086',
|
||||||
|
'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27',
|
||||||
|
'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277',
|
||||||
|
'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa',
|
||||||
|
'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e',
|
||||||
|
'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e',
|
||||||
|
'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f',
|
||||||
|
'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2',
|
||||||
|
'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc',
|
||||||
|
'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167',
|
||||||
|
'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64',
|
||||||
|
'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d',
|
||||||
|
'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20',
|
||||||
|
'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab',
|
||||||
|
'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53',
|
||||||
|
'setuptools-0.6c7-py2.3.egg': '209fdf9adc3a615e5115b725658e13e2',
|
||||||
|
'setuptools-0.6c7-py2.4.egg': '5a8f954807d46a0fb67cf1f26c55a82e',
|
||||||
|
'setuptools-0.6c7-py2.5.egg': '45d2ad28f9750e7434111fde831e8372',
|
||||||
|
'setuptools-0.6c8-py2.3.egg': '50759d29b349db8cfd807ba8303f1902',
|
||||||
|
'setuptools-0.6c8-py2.4.egg': 'cba38d74f7d483c06e9daa6070cce6de',
|
||||||
|
'setuptools-0.6c8-py2.5.egg': '1721747ee329dc150590a58b3e1ac95b',
|
||||||
|
'setuptools-0.6c9-py2.3.egg': 'a83c4020414807b496e4cfbe08507c03',
|
||||||
|
'setuptools-0.6c9-py2.4.egg': '260a2be2e5388d66bdaee06abec6342a',
|
||||||
|
'setuptools-0.6c9-py2.5.egg': 'fe67c3e5a17b12c0e7c541b7ea43a8e6',
|
||||||
|
'setuptools-0.6c9-py2.6.egg': 'ca37b1ff16fa2ede6e19383e7b59245a',
|
||||||
|
}
|
||||||
|
|
||||||
|
import sys, os
|
||||||
|
try: from hashlib import md5
|
||||||
|
except ImportError: from md5 import md5
|
||||||
|
|
||||||
|
def _validate_md5(egg_name, data):
|
||||||
|
if egg_name in md5_data:
|
||||||
|
digest = md5(data).hexdigest()
|
||||||
|
if digest != md5_data[egg_name]:
|
||||||
|
print >>sys.stderr, (
|
||||||
|
"md5 validation of %s failed! (Possible download problem?)"
|
||||||
|
% egg_name
|
||||||
|
)
|
||||||
|
sys.exit(2)
|
||||||
|
return data
|
||||||
|
|
||||||
|
def use_setuptools(
|
||||||
|
version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
|
||||||
|
download_delay=15
|
||||||
|
):
|
||||||
|
"""Automatically find/download setuptools and make it available on sys.path
|
||||||
|
|
||||||
|
`version` should be a valid setuptools version number that is available
|
||||||
|
as an egg for download under the `download_base` URL (which should end with
|
||||||
|
a '/'). `to_dir` is the directory where setuptools will be downloaded, if
|
||||||
|
it is not already available. If `download_delay` is specified, it should
|
||||||
|
be the number of seconds that will be paused before initiating a download,
|
||||||
|
should one be required. If an older version of setuptools is installed,
|
||||||
|
this routine will print a message to ``sys.stderr`` and raise SystemExit in
|
||||||
|
an attempt to abort the calling script.
|
||||||
|
"""
|
||||||
|
was_imported = 'pkg_resources' in sys.modules or 'setuptools' in sys.modules
|
||||||
|
def do_download():
|
||||||
|
egg = download_setuptools(version, download_base, to_dir, download_delay)
|
||||||
|
sys.path.insert(0, egg)
|
||||||
|
import setuptools; setuptools.bootstrap_install_from = egg
|
||||||
|
try:
|
||||||
|
import pkg_resources
|
||||||
|
except ImportError:
|
||||||
|
return do_download()
|
||||||
|
try:
|
||||||
|
pkg_resources.require("setuptools>="+version); return
|
||||||
|
except pkg_resources.VersionConflict, e:
|
||||||
|
if was_imported:
|
||||||
|
print >>sys.stderr, (
|
||||||
|
"The required version of setuptools (>=%s) is not available, and\n"
|
||||||
|
"can't be installed while this script is running. Please install\n"
|
||||||
|
" a more recent version first, using 'easy_install -U setuptools'."
|
||||||
|
"\n\n(Currently using %r)"
|
||||||
|
) % (version, e.args[0])
|
||||||
|
sys.exit(2)
|
||||||
|
else:
|
||||||
|
del pkg_resources, sys.modules['pkg_resources'] # reload ok
|
||||||
|
return do_download()
|
||||||
|
except pkg_resources.DistributionNotFound:
|
||||||
|
return do_download()
|
||||||
|
|
||||||
|
def download_setuptools(
|
||||||
|
version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
|
||||||
|
delay = 15
|
||||||
|
):
|
||||||
|
"""Download setuptools from a specified location and return its filename
|
||||||
|
|
||||||
|
`version` should be a valid setuptools version number that is available
|
||||||
|
as an egg for download under the `download_base` URL (which should end
|
||||||
|
with a '/'). `to_dir` is the directory where the egg will be downloaded.
|
||||||
|
`delay` is the number of seconds to pause before an actual download attempt.
|
||||||
|
"""
|
||||||
|
import urllib2, shutil
|
||||||
|
egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3])
|
||||||
|
url = download_base + egg_name
|
||||||
|
saveto = os.path.join(to_dir, egg_name)
|
||||||
|
src = dst = None
|
||||||
|
if not os.path.exists(saveto): # Avoid repeated downloads
|
||||||
|
try:
|
||||||
|
from distutils import log
|
||||||
|
if delay:
|
||||||
|
log.warn("""
|
||||||
|
---------------------------------------------------------------------------
|
||||||
|
This script requires setuptools version %s to run (even to display
|
||||||
|
help). I will attempt to download it for you (from
|
||||||
|
%s), but
|
||||||
|
you may need to enable firewall access for this script first.
|
||||||
|
I will start the download in %d seconds.
|
||||||
|
|
||||||
|
(Note: if this machine does not have network access, please obtain the file
|
||||||
|
|
||||||
|
%s
|
||||||
|
|
||||||
|
and place it in this directory before rerunning this script.)
|
||||||
|
---------------------------------------------------------------------------""",
|
||||||
|
version, download_base, delay, url
|
||||||
|
); from time import sleep; sleep(delay)
|
||||||
|
log.warn("Downloading %s", url)
|
||||||
|
src = urllib2.urlopen(url)
|
||||||
|
# Read/write all in one block, so we don't create a corrupt file
|
||||||
|
# if the download is interrupted.
|
||||||
|
data = _validate_md5(egg_name, src.read())
|
||||||
|
dst = open(saveto,"wb"); dst.write(data)
|
||||||
|
finally:
|
||||||
|
if src: src.close()
|
||||||
|
if dst: dst.close()
|
||||||
|
return os.path.realpath(saveto)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv, version=DEFAULT_VERSION):
|
||||||
|
"""Install or upgrade setuptools and EasyInstall"""
|
||||||
|
try:
|
||||||
|
import setuptools
|
||||||
|
except ImportError:
|
||||||
|
egg = None
|
||||||
|
try:
|
||||||
|
egg = download_setuptools(version, delay=0)
|
||||||
|
sys.path.insert(0,egg)
|
||||||
|
from setuptools.command.easy_install import main
|
||||||
|
return main(list(argv)+[egg]) # we're done here
|
||||||
|
finally:
|
||||||
|
if egg and os.path.exists(egg):
|
||||||
|
os.unlink(egg)
|
||||||
|
else:
|
||||||
|
if setuptools.__version__ == '0.0.1':
|
||||||
|
print >>sys.stderr, (
|
||||||
|
"You have an obsolete version of setuptools installed. Please\n"
|
||||||
|
"remove it from your system entirely before rerunning this script."
|
||||||
|
)
|
||||||
|
sys.exit(2)
|
||||||
|
|
||||||
|
req = "setuptools>="+version
|
||||||
|
import pkg_resources
|
||||||
|
try:
|
||||||
|
pkg_resources.require(req)
|
||||||
|
except pkg_resources.VersionConflict:
|
||||||
|
try:
|
||||||
|
from setuptools.command.easy_install import main
|
||||||
|
except ImportError:
|
||||||
|
from easy_install import main
|
||||||
|
main(list(argv)+[download_setuptools(delay=0)])
|
||||||
|
sys.exit(0) # try to force an exit
|
||||||
|
else:
|
||||||
|
if argv:
|
||||||
|
from setuptools.command.easy_install import main
|
||||||
|
main(argv)
|
||||||
|
else:
|
||||||
|
print "Setuptools version",version,"or greater has been installed."
|
||||||
|
print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)'
|
||||||
|
|
||||||
|
def update_md5(filenames):
|
||||||
|
"""Update our built-in md5 registry"""
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
for name in filenames:
|
||||||
|
base = os.path.basename(name)
|
||||||
|
f = open(name,'rb')
|
||||||
|
md5_data[base] = md5(f.read()).hexdigest()
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
data = [" %r: %r,\n" % it for it in md5_data.items()]
|
||||||
|
data.sort()
|
||||||
|
repl = "".join(data)
|
||||||
|
|
||||||
|
import inspect
|
||||||
|
srcfile = inspect.getsourcefile(sys.modules[__name__])
|
||||||
|
f = open(srcfile, 'rb'); src = f.read(); f.close()
|
||||||
|
|
||||||
|
match = re.search("\nmd5_data = {\n([^}]+)}", src)
|
||||||
|
if not match:
|
||||||
|
print >>sys.stderr, "Internal error!"
|
||||||
|
sys.exit(2)
|
||||||
|
|
||||||
|
src = src[:match.start(1)] + repl + src[match.end(1):]
|
||||||
|
f = open(srcfile,'w')
|
||||||
|
f.write(src)
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__=='__main__':
|
||||||
|
if len(sys.argv)>2 and sys.argv[1]=='--md5update':
|
||||||
|
update_md5(sys.argv[2:])
|
||||||
|
else:
|
||||||
|
main(sys.argv[1:])
|
||||||
55
setup.py
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
#!/usr/bin/env/python
|
||||||
|
"""
|
||||||
|
Setup script for PuLP added by Stuart Mitchell 2007
|
||||||
|
Copyright 2007 Stuart Mitchell
|
||||||
|
"""
|
||||||
|
from ez_setup import use_setuptools
|
||||||
|
use_setuptools()
|
||||||
|
from setuptools import setup
|
||||||
|
|
||||||
|
Description = open('README').read()
|
||||||
|
|
||||||
|
License = open('LICENSE').read()
|
||||||
|
|
||||||
|
Version = open('VERSION').read().strip()
|
||||||
|
|
||||||
|
setup(name="PuLP",
|
||||||
|
version=Version,
|
||||||
|
description="""
|
||||||
|
PuLP is an LP modeler written in python. PuLP can generate MPS or LP files
|
||||||
|
and call GLPK, COIN CLP/CBC, CPLEX, and GUROBI to solve linear
|
||||||
|
problems.
|
||||||
|
""",
|
||||||
|
long_description = Description,
|
||||||
|
license = License,
|
||||||
|
keywords = ["Optimization", "Linear Programming", "Operations Research"],
|
||||||
|
author="J.S. Roy and S.A. Mitchell",
|
||||||
|
author_email="s.mitchell@auckland.ac.nz",
|
||||||
|
url="http://pulp-or.googlecode.com/",
|
||||||
|
classifiers = ['Development Status :: 5 - Production/Stable',
|
||||||
|
'Environment :: Console',
|
||||||
|
'Intended Audience :: Science/Research',
|
||||||
|
'License :: OSI Approved :: BSD License',
|
||||||
|
'Natural Language :: English',
|
||||||
|
'Programming Language :: Python',
|
||||||
|
'Topic :: Scientific/Engineering :: Mathematics',
|
||||||
|
],
|
||||||
|
#ext_modules = [pulpCOIN],
|
||||||
|
package_dir={'':'src'},
|
||||||
|
packages = ['pulp', 'pulp.solverdir'],
|
||||||
|
package_data = {'pulp' : ["AUTHORS","LICENSE",
|
||||||
|
"pulp.cfg.linux",
|
||||||
|
"pulp.cfg.win",
|
||||||
|
"LICENSE.CoinMP.txt",
|
||||||
|
"AUTHORS.CoinMP.txt",
|
||||||
|
"README.CoinMP.txt",
|
||||||
|
],
|
||||||
|
'pulp.solverdir' : ['*','*.*']},
|
||||||
|
install_requires = ['pyparsing>=1.5.2'],
|
||||||
|
entry_points = ("""
|
||||||
|
[console_scripts]
|
||||||
|
pulptest = pulp:pulpTestAll
|
||||||
|
pulpdoctest = pulp:pulpDoctest
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
)
|
||||||
21
solvers.cfg
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
[buildout]
|
||||||
|
extends = buildout.cfg
|
||||||
|
|
||||||
|
[glpk]
|
||||||
|
recipe = zc.recipe.cmmi
|
||||||
|
url = http://ftp.gnu.org/gnu/glpk/glpk-4.42.tar.gz
|
||||||
|
|
||||||
|
[cbc]
|
||||||
|
recipe = zc.recipe.cmmi
|
||||||
|
url = http://www.coin-or.org/download/source/Cbc/Cbc-2.4.0.tgz
|
||||||
|
|
||||||
|
[coinMP]
|
||||||
|
recipe = zc.recipe.cmmi
|
||||||
|
#url = http://pulp-or.googlecode.com/files/CoinMP-1.4.0patched.tar.gz
|
||||||
|
url = http://www.coin-or.org/download/source/CoinMP/CoinMP-1.4.0.tgz
|
||||||
|
#patch = ${buildout:directory}/patches/CoinMP-1.4.0.patch
|
||||||
|
|
||||||
|
[install-coinMP]
|
||||||
|
recipe = plone.recipe.command
|
||||||
|
command = cp ${buildout:directory}/parts/coinMP/lib/* ${buildout:directory}/src/pulp/solverdir/
|
||||||
|
update-command = cp ${buildout:directory}/parts/coinMP/lib/* ${buildout:directory}/src/pulp/solverdir/
|
||||||
37
src/pulp/__init__.py
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# PuLP : Python LP Modeler
|
||||||
|
# Version 1.20
|
||||||
|
|
||||||
|
# Copyright (c) 2002-2005, Jean-Sebastien Roy (js@jeannot.org)
|
||||||
|
# Modifications Copyright (c) 2007- Stuart Anthony Mitchell (s.mitchell@auckland.ac.nz)
|
||||||
|
# $Id: __init__.py 1791 2008-04-23 22:54:34Z smit023 $
|
||||||
|
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
# copy of this software and associated documentation files (the
|
||||||
|
# "Software"), to deal in the Software without restriction, including
|
||||||
|
# without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
# permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
# the following conditions:
|
||||||
|
|
||||||
|
# The above copyright notice and this permission notice shall be included
|
||||||
|
# in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
"""
|
||||||
|
Module file that imports all of the pulp functions
|
||||||
|
|
||||||
|
Copyright 2007- Stuart Mitchell (s.mitchell@auckland.ac.nz)
|
||||||
|
"""
|
||||||
|
|
||||||
|
from pulp import *
|
||||||
|
from amply import *
|
||||||
|
__doc__ = pulp.__doc__
|
||||||
|
|
||||||
|
import tests
|
||||||
782
src/pulp/amply.py
Normal file
@@ -0,0 +1,782 @@
|
|||||||
|
#! /usr/bin/env python
|
||||||
|
# Amply: a GNU MathProg data-parser
|
||||||
|
|
||||||
|
# Copyright (c) 2010, Q. Lim (qlim001@aucklanduni.ac.nz)
|
||||||
|
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
# copy of this software and associated documentation files (the
|
||||||
|
# "Software"), to deal in the Software without restriction, including
|
||||||
|
# without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
# permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
# the following conditions:
|
||||||
|
|
||||||
|
# The above copyright notice and this permission notice shall be included
|
||||||
|
# in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
"""
|
||||||
|
Amply: a GNU MathProg data-parser
|
||||||
|
|
||||||
|
This module implements a parser for a subset of the GNU MathProg language,
|
||||||
|
namely parameter and set data records.
|
||||||
|
|
||||||
|
Amply uses the Pyparsing library to parse input:
|
||||||
|
http://pyparsing.wikispaces.com
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
Create an Amply object, optionally passing in a string to parse.
|
||||||
|
|
||||||
|
>>> a = Amply("param T := 3;")
|
||||||
|
|
||||||
|
Symbols that are defined can be accessed as attributes or items.
|
||||||
|
|
||||||
|
>>> print a.T
|
||||||
|
3
|
||||||
|
>>> print a['T']
|
||||||
|
3
|
||||||
|
|
||||||
|
The load_string and load_file methods can be used to parse additional data
|
||||||
|
|
||||||
|
>>> a.load_string("set N := 1 2 3;")
|
||||||
|
>>> a.load_file(open('some_file.dat'))
|
||||||
|
|
||||||
|
An Amply object can be constructed from a file using Amply.from_file
|
||||||
|
|
||||||
|
>>> a = Amply.from_file(open('some_file.dat'))
|
||||||
|
|
||||||
|
|
||||||
|
How it works:
|
||||||
|
The Amply class parses the input using Pyparsing. This results in a list
|
||||||
|
of Stmt objects, each representing a MathProg statement. The statements
|
||||||
|
are then evaluated by calling their eval() method.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
import pyparsing
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
from pyparsing import alphas, nums, alphanums, delimitedList, oneOf
|
||||||
|
from pyparsing import Combine, Dict, Forward, Group, Literal, NotAny
|
||||||
|
from pyparsing import OneOrMore, Optional, ParseResults, QuotedString
|
||||||
|
from pyparsing import StringEnd, Suppress, Word, ZeroOrMore
|
||||||
|
|
||||||
|
from itertools import chain
|
||||||
|
|
||||||
|
__all__ = ['Amply', 'AmplyError']
|
||||||
|
|
||||||
|
|
||||||
|
class AmplyObject(object):
|
||||||
|
"""
|
||||||
|
Represents the value of some object (e.g. a Set object
|
||||||
|
or Parameter object
|
||||||
|
"""
|
||||||
|
|
||||||
|
class AmplyStmt(object):
|
||||||
|
"""
|
||||||
|
Represents a statement that has been parsed
|
||||||
|
|
||||||
|
Statements implement an eval method. When the eval method is called, the
|
||||||
|
Stmt object is responsible for modifying the Amply object that
|
||||||
|
gets passed in appropriately (i.e. by adding or modifying a symbol)
|
||||||
|
"""
|
||||||
|
|
||||||
|
def eval(self, amply): # pragma: no coverage
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
|
||||||
|
class NoDefault(object):
|
||||||
|
"""
|
||||||
|
Sentinel
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class AmplyError(Exception):
|
||||||
|
"""
|
||||||
|
Amply Exception Class
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def chunk(it, n):
|
||||||
|
"""
|
||||||
|
Yields n-tuples from iterator
|
||||||
|
"""
|
||||||
|
c = []
|
||||||
|
for i, x in enumerate(it):
|
||||||
|
c.append(x)
|
||||||
|
if (i + 1) % n == 0:
|
||||||
|
yield tuple(c)
|
||||||
|
c = []
|
||||||
|
if c:
|
||||||
|
yield tuple(c)
|
||||||
|
|
||||||
|
|
||||||
|
def access_data(curr_dict, keys, default=NoDefault):
|
||||||
|
"""
|
||||||
|
Convenience method for walking down a series of nested dictionaries
|
||||||
|
|
||||||
|
keys is a tuple of strings
|
||||||
|
|
||||||
|
access_data(dict, ('key1', 'key2', 'key3') is equivalent to
|
||||||
|
dict['key1']['key2']['key3']
|
||||||
|
|
||||||
|
All dictionaries must exist, but the last dictionary in the hierarchy
|
||||||
|
does not have to contain the final key, if default is set.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if keys in curr_dict:
|
||||||
|
return curr_dict[keys]
|
||||||
|
|
||||||
|
if isinstance(keys, tuple):
|
||||||
|
for sym in keys[:-1]:
|
||||||
|
curr_dict = curr_dict[sym]
|
||||||
|
r = curr_dict.get(keys[-1], default)
|
||||||
|
if r is not NoDefault:
|
||||||
|
return r
|
||||||
|
|
||||||
|
if default is not NoDefault:
|
||||||
|
return default
|
||||||
|
|
||||||
|
raise KeyError()
|
||||||
|
|
||||||
|
|
||||||
|
def transpose(data):
|
||||||
|
"""
|
||||||
|
Transpose a matrix represented as a dict of dicts
|
||||||
|
"""
|
||||||
|
|
||||||
|
rows = data.keys()
|
||||||
|
cols = set()
|
||||||
|
for d in data.values():
|
||||||
|
cols.update(d.keys())
|
||||||
|
|
||||||
|
d = {}
|
||||||
|
|
||||||
|
for col in cols:
|
||||||
|
d[col] = {}
|
||||||
|
for row in rows:
|
||||||
|
d[col][row] = data[row][col]
|
||||||
|
return d
|
||||||
|
|
||||||
|
|
||||||
|
class SetDefStmt(AmplyStmt):
|
||||||
|
"""
|
||||||
|
Represents a set definition statement
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, tokens):
|
||||||
|
assert (tokens[0] == 'set')
|
||||||
|
self.name = tokens[1]
|
||||||
|
self.dimen = tokens.get('dimen', None)
|
||||||
|
self.subscripts = len(tokens.get('subscripts', ()))
|
||||||
|
|
||||||
|
def __repr__(self): # pragma: no cover
|
||||||
|
return '<%s: %s[%s]>' % (self.__class__.__name__, self.name,
|
||||||
|
self.dimen)
|
||||||
|
|
||||||
|
def eval(self, amply):
|
||||||
|
set_obj = SetObject(subscripts=self.subscripts, dimen=self.dimen)
|
||||||
|
amply._addSymbol(self.name, set_obj)
|
||||||
|
|
||||||
|
|
||||||
|
class SetStmt(AmplyStmt):
|
||||||
|
"""
|
||||||
|
Represents a set statement
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, tokens):
|
||||||
|
assert(tokens[0] == 'set')
|
||||||
|
self.name = tokens[1]
|
||||||
|
self.records = tokens.get('records')
|
||||||
|
self.member = tokens.get('member', None)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<%s: %s[%s] = %s>' % (self.__class__.__name__, self.name,
|
||||||
|
self.member, self.records)
|
||||||
|
|
||||||
|
def eval(self, amply):
|
||||||
|
if self.name in amply.symbols:
|
||||||
|
obj = amply.symbols[self.name]
|
||||||
|
assert isinstance(obj, SetObject)
|
||||||
|
else:
|
||||||
|
obj = SetObject()
|
||||||
|
|
||||||
|
obj.addData(self.member, self.records)
|
||||||
|
amply._addSymbol(self.name, obj)
|
||||||
|
|
||||||
|
|
||||||
|
class SliceRecord(object):
|
||||||
|
"""
|
||||||
|
Represents a parameter or set slice record
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, tokens):
|
||||||
|
self.components = tuple(tokens)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<%s: %s>' % (self.__class__.__name__, self.components)
|
||||||
|
|
||||||
|
|
||||||
|
class TabularRecord(object):
|
||||||
|
"""
|
||||||
|
Represents a parameter tabular record
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, tokens):
|
||||||
|
self._columns = tokens.columns
|
||||||
|
self._data = tokens.data
|
||||||
|
self.transposed = False
|
||||||
|
|
||||||
|
def setTransposed(self, t):
|
||||||
|
self.transposed = t
|
||||||
|
|
||||||
|
def _rows(self):
|
||||||
|
c = Chunker(self._data)
|
||||||
|
while c.notEmpty():
|
||||||
|
row_label = c.chunk()
|
||||||
|
data = c.chunk(len(self._columns))
|
||||||
|
yield row_label, data
|
||||||
|
|
||||||
|
def data(self):
|
||||||
|
d = {}
|
||||||
|
for row, data in self._rows():
|
||||||
|
d[row] = {}
|
||||||
|
for col, value in zip(self._columns, data):
|
||||||
|
d[row][col] = value
|
||||||
|
if self.transposed:
|
||||||
|
return transpose(d)
|
||||||
|
else:
|
||||||
|
return d
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<%s: %s>' % (self.__class__.__name__, self.data())
|
||||||
|
|
||||||
|
|
||||||
|
class MatrixData(TabularRecord):
|
||||||
|
"""
|
||||||
|
Represents a set matrix data record
|
||||||
|
"""
|
||||||
|
|
||||||
|
def _rows(self):
|
||||||
|
for row in self._data:
|
||||||
|
yield row[0], row[1:]
|
||||||
|
|
||||||
|
def data(self):
|
||||||
|
d = []
|
||||||
|
for row_label, data in self._rows():
|
||||||
|
for col, value in zip(self._columns, data):
|
||||||
|
if value == '+':
|
||||||
|
if self.transposed:
|
||||||
|
d.append((col, row_label))
|
||||||
|
else:
|
||||||
|
d.append((row_label, col))
|
||||||
|
return d
|
||||||
|
|
||||||
|
|
||||||
|
class ParamStmt(AmplyStmt):
|
||||||
|
"""
|
||||||
|
Represents a parameter statement
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, tokens):
|
||||||
|
assert(tokens[0] == 'param')
|
||||||
|
self.name = tokens.name
|
||||||
|
self.records = tokens.records
|
||||||
|
self.default = tokens.get('default', 0)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<%s: %s = %s>' % (self.__class__.__name__, self.name,
|
||||||
|
self.records)
|
||||||
|
|
||||||
|
def eval(self, amply):
|
||||||
|
if self.name in amply.symbols:
|
||||||
|
obj = amply.symbols[self.name]
|
||||||
|
assert isinstance(obj, ParamObject)
|
||||||
|
else:
|
||||||
|
obj = ParamObject()
|
||||||
|
|
||||||
|
if obj.subscripts == 0:
|
||||||
|
assert len(self.records) == 1
|
||||||
|
assert len(self.records[0]) == 1
|
||||||
|
amply._addSymbol(self.name, self.records[0][0])
|
||||||
|
else:
|
||||||
|
obj.addData(self.records.asList(), default=self.default)
|
||||||
|
|
||||||
|
amply._addSymbol(self.name, obj)
|
||||||
|
|
||||||
|
|
||||||
|
class Chunker(object):
|
||||||
|
"""
|
||||||
|
Chunker class - used to consume tuples from
|
||||||
|
an iterator
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, it):
|
||||||
|
"""
|
||||||
|
it is a sequence or iterator
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.it = iter(it)
|
||||||
|
self.empty = False
|
||||||
|
self.next = None
|
||||||
|
self._getNext()
|
||||||
|
|
||||||
|
def _getNext(self):
|
||||||
|
"""
|
||||||
|
basically acts as a 1 element buffer so that
|
||||||
|
we can detect if we've reached the end of the
|
||||||
|
iterator
|
||||||
|
"""
|
||||||
|
|
||||||
|
old = self.next
|
||||||
|
try:
|
||||||
|
self.next = self.it.next()
|
||||||
|
except StopIteration:
|
||||||
|
self.empty = True
|
||||||
|
return old
|
||||||
|
|
||||||
|
def notEmpty(self):
|
||||||
|
"""
|
||||||
|
Test if the iterator has reached the end
|
||||||
|
"""
|
||||||
|
|
||||||
|
return not self.empty
|
||||||
|
|
||||||
|
def chunk(self, n=None):
|
||||||
|
"""
|
||||||
|
Return a list with the next n elements from the iterator,
|
||||||
|
or the next element if n is None
|
||||||
|
"""
|
||||||
|
if n is None:
|
||||||
|
return self._getNext()
|
||||||
|
return [self._getNext() for i in range(n)]
|
||||||
|
|
||||||
|
|
||||||
|
class ParamTabbingStmt(AmplyStmt):
|
||||||
|
"""
|
||||||
|
Represents a parameter tabbing data statement
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, tokens):
|
||||||
|
assert(tokens[0] == 'param')
|
||||||
|
|
||||||
|
self.default = tokens.get('default', 0)
|
||||||
|
self.params = tokens.params
|
||||||
|
self.data = tokens.data
|
||||||
|
|
||||||
|
def eval(self, amply):
|
||||||
|
for i, param_name in enumerate(self.params):
|
||||||
|
if param_name in amply.symbols:
|
||||||
|
obj = amply.symbols[param_name]
|
||||||
|
else:
|
||||||
|
raise AmplyError("Param %s not previously defined" %
|
||||||
|
param_name)
|
||||||
|
|
||||||
|
for subs, data in self._rows(obj.subscripts):
|
||||||
|
obj.setValue(subs, data[i])
|
||||||
|
|
||||||
|
def _rows(self, n_subscripts):
|
||||||
|
c = Chunker(self.data)
|
||||||
|
while c.notEmpty():
|
||||||
|
subscripts = c.chunk(n_subscripts)
|
||||||
|
data = c.chunk(len(self.params))
|
||||||
|
yield (subscripts, data)
|
||||||
|
|
||||||
|
|
||||||
|
class ParamDefStmt(AmplyStmt):
|
||||||
|
"""
|
||||||
|
Represents a parameter definition
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, tokens):
|
||||||
|
assert(tokens[0] == 'param')
|
||||||
|
self.name = tokens[1]
|
||||||
|
self.subscripts = tokens.get('subscripts')
|
||||||
|
self.default = tokens.get('default', NoDefault)
|
||||||
|
|
||||||
|
def eval(self, amply):
|
||||||
|
|
||||||
|
def _getDimen(symbol):
|
||||||
|
s = amply[symbol]
|
||||||
|
if s is None or s.dimen is None:
|
||||||
|
return 1
|
||||||
|
return s.dimen
|
||||||
|
num_subscripts = sum(_getDimen(s) for s in self.subscripts)
|
||||||
|
amply._addSymbol(self.name, ParamObject(num_subscripts, self.default))
|
||||||
|
|
||||||
|
|
||||||
|
class ParamObject(AmplyObject):
|
||||||
|
|
||||||
|
def __init__(self, subscripts=0, default=NoDefault):
|
||||||
|
self.subscripts = subscripts
|
||||||
|
self.default = default
|
||||||
|
|
||||||
|
self.data = {}
|
||||||
|
|
||||||
|
# initial slice is all *'s
|
||||||
|
self._setSlice(SliceRecord(['*'] * self.subscripts))
|
||||||
|
|
||||||
|
def addData(self, data, default=0):
|
||||||
|
|
||||||
|
def _v(v):
|
||||||
|
if v == '.':
|
||||||
|
return default
|
||||||
|
return v
|
||||||
|
|
||||||
|
for record in data:
|
||||||
|
if isinstance(record, SliceRecord):
|
||||||
|
self._setSlice(record)
|
||||||
|
elif isinstance(record, list):
|
||||||
|
# a plain data record
|
||||||
|
rec_len = len(self.free_indices) + 1
|
||||||
|
if len(record) % rec_len != 0:
|
||||||
|
raise AmplyError("Incomplete data record, expecting %d"
|
||||||
|
" subscripts per value" %
|
||||||
|
len(self.free_indices))
|
||||||
|
for c in chunk(record, len(self.free_indices) + 1):
|
||||||
|
self.setValue(c[:-1], _v(c[-1]))
|
||||||
|
elif isinstance(record, TabularRecord):
|
||||||
|
record_data = record.data()
|
||||||
|
for row_symbol in record_data:
|
||||||
|
for col_symbol, value in record_data[row_symbol].items():
|
||||||
|
self.setValue((row_symbol, col_symbol), _v(value))
|
||||||
|
|
||||||
|
def _setSlice(self, slice):
|
||||||
|
self.current_slice = list(slice.components) #copy
|
||||||
|
self.free_indices = [i for i, v in enumerate(self.current_slice)
|
||||||
|
if v == '*']
|
||||||
|
|
||||||
|
def setValue(self, symbols, value):
|
||||||
|
if value == '.':
|
||||||
|
value = self.default
|
||||||
|
|
||||||
|
assert len(symbols) == len(self.free_indices)
|
||||||
|
symbol_path = self.current_slice
|
||||||
|
for index, symbol in zip(self.free_indices, symbols):
|
||||||
|
symbol_path[index] = symbol
|
||||||
|
|
||||||
|
curr_dict = self.data
|
||||||
|
for symbol in symbol_path[:-1]:
|
||||||
|
if symbol not in curr_dict:
|
||||||
|
curr_dict[symbol] = {}
|
||||||
|
curr_dict = curr_dict[symbol]
|
||||||
|
curr_dict[symbol_path[-1]] = value
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
return access_data(self.data, key, self.default)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<%s: %s>' % (self.__class__.__name__, self.data)
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return self.data == other
|
||||||
|
|
||||||
|
def __ne__(self, other):
|
||||||
|
return self.data != other
|
||||||
|
|
||||||
|
|
||||||
|
class SetObject(AmplyObject):
|
||||||
|
|
||||||
|
def __init__(self, subscripts=0, dimen=None):
|
||||||
|
self.dimen = dimen
|
||||||
|
self.subscripts = subscripts
|
||||||
|
|
||||||
|
if self.subscripts == 0:
|
||||||
|
self.data = []
|
||||||
|
else:
|
||||||
|
self.data = {}
|
||||||
|
|
||||||
|
self.current_slice = None
|
||||||
|
|
||||||
|
def addData(self, member, data):
|
||||||
|
dest_list = self._memberList(member)
|
||||||
|
|
||||||
|
if self.dimen is not None and self.current_slice is None:
|
||||||
|
self._setSlice(['*'] * self.dimen)
|
||||||
|
|
||||||
|
for record in data:
|
||||||
|
if isinstance(record, SliceRecord):
|
||||||
|
self._setSlice(record.components)
|
||||||
|
elif isinstance(record, MatrixData):
|
||||||
|
if self.dimen is None:
|
||||||
|
self.dimen = 2
|
||||||
|
self._setSlice(['*'] * 2)
|
||||||
|
d = record.data()
|
||||||
|
for v in d:
|
||||||
|
self._addValue(dest_list, v)
|
||||||
|
|
||||||
|
else: # simple-data
|
||||||
|
self._addSimpleData(dest_list, record)
|
||||||
|
|
||||||
|
def _setSlice(self, slice):
|
||||||
|
self.current_slice = slice
|
||||||
|
self.free_indices = [i for i, v in enumerate(self.current_slice)
|
||||||
|
if v == '*']
|
||||||
|
|
||||||
|
def _memberList(self, member):
|
||||||
|
if member is None:
|
||||||
|
return self.data
|
||||||
|
assert len(member) == self.subscripts
|
||||||
|
|
||||||
|
curr_dict = self.data
|
||||||
|
for symbol in member[:-1]:
|
||||||
|
if symbol not in curr_dict:
|
||||||
|
curr_dict[symbol] = {}
|
||||||
|
curr_dict = curr_dict[symbol]
|
||||||
|
if member[-1] not in curr_dict:
|
||||||
|
curr_dict[member[-1]] = []
|
||||||
|
return curr_dict[member[-1]]
|
||||||
|
|
||||||
|
def _dataLen(self, d):
|
||||||
|
if isinstance(d, (tuple, list)):
|
||||||
|
return len(d)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
def _addSimpleData(self, data_list, data):
|
||||||
|
if isinstance(data[0], ParseResults):
|
||||||
|
inferred_dimen = len(data[0])
|
||||||
|
else:
|
||||||
|
inferred_dimen = 1
|
||||||
|
|
||||||
|
if self.dimen == None:
|
||||||
|
# infer dimension from records
|
||||||
|
self.dimen = inferred_dimen
|
||||||
|
|
||||||
|
if self.current_slice == None:
|
||||||
|
self._setSlice(tuple(['*'] * self.dimen))
|
||||||
|
|
||||||
|
if len(self.free_indices) == inferred_dimen:
|
||||||
|
for d in data.asList():
|
||||||
|
self._addValue(data_list, d)
|
||||||
|
elif len(self.free_indices) > 1 and inferred_dimen:
|
||||||
|
for c in chunk(data, len(self.free_indices)):
|
||||||
|
self._addValue(data_list, c)
|
||||||
|
else:
|
||||||
|
raise AmplyError("Dimension of elements (%d) does not match "
|
||||||
|
"declared dimension, (%d)" %
|
||||||
|
(inferred_dimen, self.dimen))
|
||||||
|
|
||||||
|
def _addValue(self, data_list, item):
|
||||||
|
if self.dimen == 1:
|
||||||
|
data_list.append(item)
|
||||||
|
else:
|
||||||
|
assert len(self.free_indices) == self._dataLen(item)
|
||||||
|
|
||||||
|
to_add = list(self.current_slice)
|
||||||
|
if isinstance(item, (tuple, list)):
|
||||||
|
for index, value in zip(self.free_indices, item):
|
||||||
|
to_add[index] = value
|
||||||
|
else:
|
||||||
|
assert len(self.free_indices) == 1
|
||||||
|
to_add[self.free_indices[0]] = item
|
||||||
|
data_list.append(tuple(to_add))
|
||||||
|
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
if not self.subscripts:
|
||||||
|
return self.data[key]
|
||||||
|
return access_data(self.data, key)
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self.data)
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return iter(self.data)
|
||||||
|
|
||||||
|
def __contains__(self, item):
|
||||||
|
return item in self.data
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return self.data == other
|
||||||
|
|
||||||
|
def __ne__(self, other):
|
||||||
|
return self.data != other
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<%s: %s>' % (self.__class__.__name__, self.data)
|
||||||
|
|
||||||
|
def mark_transposed(tokens):
|
||||||
|
tokens[0].setTransposed(True)
|
||||||
|
return tokens
|
||||||
|
|
||||||
|
|
||||||
|
# What follows is a Pyparsing description of the grammar
|
||||||
|
|
||||||
|
symbol = Word(alphas, alphanums + "_")
|
||||||
|
sign = Optional(oneOf("+ -"))
|
||||||
|
integer = Combine(sign + Word(nums)).setParseAction(lambda t: int(t[0]))
|
||||||
|
number = Combine(Word( "+-"+nums, nums) +
|
||||||
|
Optional("." + Optional(Word(nums))) +
|
||||||
|
Optional(oneOf("e E") + Word("+-"+nums, nums)))\
|
||||||
|
.setParseAction(lambda t: float(t[0]))
|
||||||
|
|
||||||
|
LPAREN = Suppress('(')
|
||||||
|
RPAREN = Suppress(')')
|
||||||
|
LBRACE = Suppress('{')
|
||||||
|
RBRACE = Suppress('}')
|
||||||
|
LBRACKET = Suppress('[')
|
||||||
|
RBRACKET = Suppress(']')
|
||||||
|
END = Suppress(';')
|
||||||
|
|
||||||
|
PLUS = Literal('+')
|
||||||
|
MINUS = Literal('-')
|
||||||
|
|
||||||
|
single = number | symbol | QuotedString('"') | QuotedString("'")
|
||||||
|
tuple_ = Group(LPAREN + delimitedList(single) + RPAREN)
|
||||||
|
subscript_domain = LBRACE + Group(delimitedList(symbol)) \
|
||||||
|
.setResultsName('subscripts') + RBRACE
|
||||||
|
|
||||||
|
data = single | tuple_
|
||||||
|
|
||||||
|
# should not match a single (tr)
|
||||||
|
simple_data = Group(NotAny('(tr)') + data + ZeroOrMore(Optional(Suppress(',')) + data))
|
||||||
|
# the first element of a set data record cannot be 'dimen', or else
|
||||||
|
# these would match set_def_stmts
|
||||||
|
non_dimen_simple_data = ~Literal('dimen') + simple_data
|
||||||
|
|
||||||
|
matrix_row = Group(single + OneOrMore(PLUS | MINUS))
|
||||||
|
matrix_data = ":" + OneOrMore(single).setResultsName('columns') \
|
||||||
|
+ ":=" + OneOrMore(matrix_row).setResultsName('data')
|
||||||
|
matrix_data.setParseAction(MatrixData)
|
||||||
|
|
||||||
|
tr_matrix_data = Suppress("(tr)") + matrix_data
|
||||||
|
tr_matrix_data.setParseAction(mark_transposed)
|
||||||
|
|
||||||
|
set_slice_component = number | symbol | '*'
|
||||||
|
set_slice_record = LPAREN + NotAny('tr') + delimitedList(set_slice_component) + RPAREN
|
||||||
|
set_slice_record.setParseAction(SliceRecord)
|
||||||
|
|
||||||
|
_set_record = set_slice_record | matrix_data | tr_matrix_data | Suppress(":=")
|
||||||
|
set_record = simple_data | _set_record
|
||||||
|
non_dimen_set_record = non_dimen_simple_data | _set_record
|
||||||
|
|
||||||
|
set_def_stmt = "set" + symbol + Optional(subscript_domain) + \
|
||||||
|
Optional("dimen" + integer.setResultsName('dimen')) + END
|
||||||
|
set_def_stmt.setParseAction(SetDefStmt)
|
||||||
|
|
||||||
|
set_member = LBRACKET + delimitedList(data) + RBRACKET
|
||||||
|
|
||||||
|
set_stmt = "set" + symbol + Optional(set_member).setResultsName("member") + \
|
||||||
|
Group(non_dimen_set_record + ZeroOrMore(Optional(Suppress(',')) + set_record)) \
|
||||||
|
.setResultsName("records") + END
|
||||||
|
set_stmt.setParseAction(SetStmt)
|
||||||
|
|
||||||
|
subscript = single
|
||||||
|
|
||||||
|
param_data = data | '.'
|
||||||
|
plain_data = param_data | subscript + ZeroOrMore(Optional(Suppress(',')) +
|
||||||
|
subscript) + param_data
|
||||||
|
# should not match a single (tr)
|
||||||
|
plain_data_record = Group(NotAny('(tr)') + plain_data + NotAny(plain_data) | \
|
||||||
|
plain_data + OneOrMore(plain_data) + NotAny(plain_data))
|
||||||
|
|
||||||
|
tabular_record = ":" + OneOrMore(single).setResultsName("columns") \
|
||||||
|
+ ":=" + OneOrMore(single | '.').setResultsName('data')
|
||||||
|
tabular_record.setParseAction(TabularRecord)
|
||||||
|
|
||||||
|
tr_tabular_record = Suppress("(tr)") + tabular_record
|
||||||
|
tr_tabular_record.setParseAction(mark_transposed)
|
||||||
|
|
||||||
|
param_slice_component = number | symbol | '*'
|
||||||
|
param_slice_record = LBRACKET + delimitedList(param_slice_component) + RBRACKET
|
||||||
|
param_slice_record.setParseAction(SliceRecord)
|
||||||
|
|
||||||
|
param_record = param_slice_record | plain_data_record | tabular_record | \
|
||||||
|
tr_tabular_record | Suppress(":=")
|
||||||
|
|
||||||
|
param_default = Optional("default" + data.setResultsName('default'))
|
||||||
|
|
||||||
|
param_stmt = "param" + symbol.setResultsName('name') + param_default + \
|
||||||
|
Group(OneOrMore(param_record)).setResultsName('records') + END
|
||||||
|
param_stmt.setParseAction(ParamStmt)
|
||||||
|
|
||||||
|
param_tabbing_stmt = "param" + param_default + ':' + Optional(symbol + ':') + \
|
||||||
|
OneOrMore(data).setResultsName('params') \
|
||||||
|
+ ':=' + OneOrMore(single).setResultsName('data') + END
|
||||||
|
param_tabbing_stmt.setParseAction(ParamTabbingStmt)
|
||||||
|
|
||||||
|
param_def_stmt = "param" + symbol + Optional(subscript_domain) + \
|
||||||
|
param_default + END
|
||||||
|
param_def_stmt.setParseAction(ParamDefStmt)
|
||||||
|
|
||||||
|
stmts = set_stmt | set_def_stmt | param_stmt | param_def_stmt | \
|
||||||
|
param_tabbing_stmt
|
||||||
|
grammar = ZeroOrMore(stmts) + StringEnd()
|
||||||
|
|
||||||
|
|
||||||
|
class Amply(object):
|
||||||
|
"""
|
||||||
|
Data parsing interface
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, s=""):
|
||||||
|
"""
|
||||||
|
Create an Amply parser instance
|
||||||
|
|
||||||
|
@param s (default ""): initial string to parse
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.symbols = {}
|
||||||
|
|
||||||
|
self.load_string(s)
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
"""
|
||||||
|
Override so that symbols can be accessed using
|
||||||
|
[] subscripts
|
||||||
|
"""
|
||||||
|
if key in self.symbols:
|
||||||
|
return self.symbols[key]
|
||||||
|
|
||||||
|
def __getattr__(self, name):
|
||||||
|
"""
|
||||||
|
Override so that symbols can be accesed as attributes
|
||||||
|
"""
|
||||||
|
if name in self.symbols:
|
||||||
|
return self.symbols[name]
|
||||||
|
return super(Amply, self).__getattr__(name)
|
||||||
|
|
||||||
|
def _addSymbol(self, name, value):
|
||||||
|
"""
|
||||||
|
Adds a symbol to this instance.
|
||||||
|
|
||||||
|
Typically, this class is called by objects created by
|
||||||
|
the parser, and should not need to be called by users
|
||||||
|
directly
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.symbols[name] = value
|
||||||
|
|
||||||
|
def load_string(self, string):
|
||||||
|
"""
|
||||||
|
Load and parse string
|
||||||
|
|
||||||
|
@param string string to parse
|
||||||
|
"""
|
||||||
|
for obj in grammar.parseString(string):
|
||||||
|
obj.eval(self)
|
||||||
|
|
||||||
|
def load_file(self, f):
|
||||||
|
"""
|
||||||
|
Load and parse file
|
||||||
|
|
||||||
|
@param f file-like object
|
||||||
|
"""
|
||||||
|
self.load_string(f.read())
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_file(f):
|
||||||
|
"""
|
||||||
|
Create a new Amply instance from file (factory method)
|
||||||
|
|
||||||
|
@param f file-like object
|
||||||
|
"""
|
||||||
|
return Amply(f.read())
|
||||||
|
|
||||||