OptimJ GUI
A graphical user interface for LP/MIP solvers
|
|
|
OptimJ GUI is a free graphical user interface (GUI) for LP/MIP solvers currently supporting lp_solve (more target solvers to come). Its main features are:
- visualize in real time the internal lp_solve state and objective value
- parameterize lp_solve with various settings
- display in real time the model structure
OptimJ GUI will help you:
- debug and tune your models
- quickly develop graphical prototypes that you can show to prospective clients or fellow team members
Operating modes
OptimJ GUI can operate either as a stand-alone Java™ tool or in combination with OptimJ™.
When used as a stand-alone tool, OptimJ GUI can display in real time all the lp_solve internal state. Simply add one line to your existing Java code:
When used in combination with OptimJ, OptimJ GUI will additionally display the model structure in terms of the data structures defined in the OptimJ source. It can display both decision variables (the unknown of the model) and programming variables (the parameters of the model).
Overview
This page is organized as follows:- The first part presents OptimJ GUI as a stand-alone tool.
- The second part presents advanced features available only for OptimJ models.
- The appendix presents the full list of properties given in this article.
This is a beta version. This documentation refers to the RC2 beta version of OptimJ GUI. APIs are subject to change. Comments and suggestions for improvements are welcome in the OptimJ GUI forum.
While the current version only works with lp_solve, OptimJ GUI will progressively be targeted to all solvers supported by OptimJ.
Part 1: Using OptimJ GUI as a stand-alone tool
When used as a stand-alone tool, OptimJ GUI provides:
- visual prototyping
- visual debugging
- interactive parameter settings
- with exactly one line of code
If you're familiar with lp_solve, you'll notice that OptimJ GUI bears some ressemblance with LPSolve IDE. There are however major differences:
- LPSolve IDE calls lp_solve, while OptimJ GUI is called by the user code. This means better integration with your application. Put shortly, you can think of OptimJ GUI as an enhanced debugging tool.
- The input to LPSolve IDE is an LP file, while the "input" to OptimJ GUI is the user code (which may read an LP file if needed).
- When used in conjunction
with OptimJ, the GUI can display data in terms of the data structures
defined in a model, rather than in terms of the internal lp_solve
matrix.
This proves very useful for getting a clear understanding of what is going on. This also provides rapid protyping for free: write a model as usual, add one single line of code, et voila, you have a visual prototype that you can show to your client or to your boss. - OptimJ GUI displays some additional information, such as a real time graph of the current objective value.
- OptimJ GUI incurs very little runtime overhead.
Just one line
In order to use OptimJ GUI, the only change that is required to your code is the addition of a single line:
OptimJ GUI comes with a set of samples that contains examples of code for running and displaying models written in lp or mps file formats. If you're not familiar with Java, simply copy-and-paste these samples.
Main window
The top part of the GUI window is a "control panel" that presents an overview of the model and the solver state:
- 'Control' buttons allow one to pause or step the solver
- 'Solver activity' displays the current objective value, the number of simplex and B&B iterations, and the running time of the solver
- 'Model size' summarizes the number of variables and constraints before and after presolve
In the bottom part of the window, the first three tabs relate to the lp_solve state:
- Run
- Parameters
- Matrix Data
The last tab displays the model data as defined in the OptimJ code. This tab is the subject of the second part of this article.
Debugging and tuning a model
The 'Run' tab displays in real-time:
- the current objective value and its evolution
- solver events
- the log messages of the solver
Visualizing the evolution of the objective function is
helpful in understanding where solving time is spent. As an example, on a
specific model, if the branch-and-bound algorithm does not show any progress
after a first solution, this may be a hint that this first solution is a
good candidate and that there is no need waiting for a provably optimal
solution.
The 'Parameters' tab provides an interactive way to experiment with various settings, such as:
- presolve mode
- scaling options
- branch and bound strategy
The 'Matrix data' tab displays in real-time the model structure as seen by the solver:
- the coefficients of the matrix
- the values and duals of the constraints
- the lower bounds, upper bounds, values and duals of the variables
Part 2: Using OptimJ GUI with OptimJ models
OptimJ is an extension of the Java programming language with language support for writing optimization models and powerful abstractions for bulk data processing. All standard Java libraries and tools are directly available. The language is supported by programming tools under the Eclipse environment.
When used in combination with OptimJ, the GUI will display a new tab called Model Data. This tab displays the model in terms of the data structures defined in the OptimJ source. It can display variables (the unknown of the model) and programming variables (the parameters of the model).
A running example: the transportation problem
Our running example will be the transportation model, a canonical example that appears in the first pages of most textbook about optimization techniques. The transportation problem consists in determining the quantity of product to be supplied from each factory to each customer in order to minimize the total cost.
In the following, we will focus on the transportation cost from a factory to customer. We model this information with a 2-dimensional associative array. Here is how this associative array is declared in OptimJ (code samples on light yellow background indicate OptimJ code) :
// Transportation cost from a factory to customer (in dollar per unit)
The OptimJ compiler will then use its knowledge of the structure of the associative array cost to generate a default table viewer for it.
How to get the code samplesAny example of this tutorial may be downloaded here (2Mo). To install this sample project in your Eclipse workspace, follow these steps:
- Copy the file to your computer
- Open Eclipse
- Select "File" > "Import..."
- Select "General" > "Existing Projects into Workspace" and click on "Next"
- Select "Select archive file:"
- Set the name of the archive file
- Click on "Finish"
- The samples are in your workspace
In addition to the examples you will find all the libraries required to launch the GUI:
You will need a valid OptimJ license for compiling this model. Evaluation licenses can be obtained here.
OptimJ generates a default viewer for each model
When OptimJ compiles a model, it has a full understanding of all its structure. The compiler uses this information to generate default viewers for all fields declared in the model. We will see later how to customize these default viewers.
All tables are grouped together in a type named DefaultViewersContainer (an implicit member class of the model).
For exemple, the array cost defined previously is displayed as a two dimensional structure indexed by factories (left) and customers (top).
Once the code is generated, it can be used directly in your application. Here is how to use the default container for the transportation model.
ViewersContainer viewersContainer =
// bind the solver and OptimJ GUI, providing the default viewer container.
LpSolveGUI.bind(model.solver(), viewersContainer);
In constrast, using a visualization API may require 100's of lines of code for the same result. Here, the bulk of the work is done by the OptimJ compiler at compile time.
Now let's see how to customize default viewers.
Customizing viewers
The generated default viewer container can be customized in arbitrary ways with little additional code. First, define an OptimJ class that extends DefaultViewersContainer:
extends TransportationProblem.DefaultViewersContainer {
In order to use this customized viewer container, instantiate CustomizedViewersContainer instead of the default viewers:
LpSolveGUI.bind(model.solver(), viewersContainer);
We are now ready to customize the display.
How to customize the displayed text
Below is the result we want to obtain, with a $ sign displayed in front of each number:
We are going to enhance the class CustomizedViewersContainer to obtain this result:
Relationship between model data and their table viewers
- an array cost is defined in the model
- the corresponding table viewer cost has been defined by the OptimJ compiler as a field of DefaultViewersContainer
- the model is accessible from DefaultViewersContainer via the field this.instance
Seen from CustomizedViewersContainer, this.cost is the table viewer of this.instance.cost.
Each table viewer has a number of properties. Property element handles everything related to the display of internal cells:
element itself has a property texter that controls the text contents of a cell:
The full list of viewer properties is given in the appendix.
The type of the texter is Texter2 (since array cost has two dimensions), an interface that can be
instanciated by providing a method text(Factory f, Customer c).
The text displayed in the cell will be the value returned by this method. Here is
how to do it:
// return the text to be displayed
How to colorize table cells
Here we will color in red cells that have a cost of more than $15.
By default, each cell of the table is an SWT label. The decorator property provides a hook for customizing this label.
Within the decorate()
method, all SWT API methods can be called on a label to customize it in
arbitrary ways. The following code sets the background color depending on
the value of instance.cost[f][c]:
// use the SWT API to decorate label l
if (instance.cost[f][c] >= 15) {
Here is the resulting display:
if statement in the example above has two branches, one for
coloring in red, and one for coloring in white, even though white is the
default background color. Removing the branch for white may lead to
unexpected red cells.
How to use other SWT controls
The creator property can be used to provide an arbitrary SWT Control. This makes it possible to display not only labels, but also combos, buttons, canvas, lists, etc. You can even embed arbitrary controls such as pie-charts or bar-charts using any SWT-compatible charts library.
In the following example, each cell is displayed as an SWT Button, checked if the cost is higher than $15.
Here's how to use the creator property for this example:
this.cost.element.creator = new Creator2<Factory, Customer> () {
public Control create(Composite parent) {
// here we decorate the control for a given Factory and Customer
public void decorate(Control myControl, Factory f, Customer c) {
// make the button selected if cost higher than 15
b.setSelection(instance.cost[f][c] > 15);
Below are two examples made using the creator property with standard Java visualization components.
|
|
|
How to customize indexes
Indexes are the cells that appear as row and column headers. The OptimJ GUI generates an arbitrary default layout when multiple indexes are present. For example, table cost is displayed with factories of the left (y-dimension) and customers on the top (x-dimension). It is possible to reorder indexes differently. We will see here how to customize indexes in order to bring factories on the top and customers on the left.
Each dimension of an array is represented by an index. Indexes are used for:
- Specifying the layout of dimensions (which dimensions go to the left and to the top, and in which order)
- Customizing the display of a specific dimension
In other words, an index can be used individually to change its properties, and also in combination with other indexes in order to configure the table layout.
Each array dimension has a corresponding index. Getting back to our array cost,
- cost is defined in the model as a 2-dimensional associative array.
- two indexes have been generated by the OptimJ compiler as a field of table cost.
- they are named index0 and index1, in the same order as dimensions appear in the source code.
Thus, seen from the CustomizedViewersContainer, this.cost.index0 is the index of the first dimension (the 'Factory dimension') of this.instance.cost; while this.cost.index1 refers to the 'Customer dimension'.
Note that indexes are generated not only for arrays (legacy or associative), but also for all collections. For instance, java.lang.Set and java.lang.List are considered as one-dimensional structures for display purposes.
Customizing the table layout
The two following examples change the position of the indexes inside the table and then reorganize the display of table cost.
Property xIndexes of a table contains indexes that will be displayed on the top of the table. The order is important: the first index will be the outer most. Similarly, the property yIndexes contains indexes displayed on the left.
This code puts customers (index0) on the X axis, and factories (index1) on the Y axis.
cost.xIndexes = new Index[]{cost.index0};
cost.yIndexes = new Index[]{cost.index1};
This code puts both customers and factories on the Y axis, in the order specified:
cost.xIndexes = new Index[]{};
cost.yIndexes = new Index[]{cost.index0, cost.index1};
The screenshots below show all these combinations:
- Left top, the default viewer generated by OptimJ.
- Left bottom, the two indexes have been switched.
- Right, the two indexes on the Y axis, with factories outermost.
Like element cells, index cells have a texter property for customizing the displayed text. For example, here is how to customize the text associated for a factory:
Other table properties
There are many more properties for customizing viewers, the full list is given in the appendix.
By default, the name of the table cost is 'cost: double[Customer][Factory]'. You can change it with the following code:
Yet other properties define the size of cells:
cost.rowHeight = 40;
cost.columnWidth = 100;
cost.xIndexHeight = 150;
cost.yIndexWidth = 70;
Specifiying the viewers to be displayed
By default, viewers are displayed for all fields of a model that are not private and not static, including fields inherited from the super-types (shadowed fields are excluded). If the type of a given field is array, associative array, Collection or Map, it has its own viewer. Otherwise the field is displayed in a special table named scalars.
For a large model, this can amount to an information overhead, especially if you're designing a prototype to be shown to the client. In order to show only the relevant information, the viewer property makes it possible to specify the set of viewers that will be displayed.
The following code states that only the fields cost and quantity should be displayed.
null, // insert a separator in the drop down list.
quantity // second table in the drop-down list.
Reusing properties
Quite often, you'll want to set identical values to many different properties, such as having all cell text displayed with the same font. You can do this simply by assigning the same object to different properties.
For instance, here is how you would reuse the same texter for all indexes:
cost.index0.texter = texter;
cost.index1.texter = texter;
Advanced features
Callbacks
OptimJ GUI uses callbacks for displaying solver information in real time. These callbacks may interfere with those in your code. This is due to the lp_solve API design: registering a callbacks removes any previously registered one, without notice. There is no simple solution at this moment, the safest approach is to disable your callbacks while using OptimJ GUI.
Detecting the termination of solve()
lp_solve callbacks do not send events for the start and the end of the solve method. As a result, some features such as the tabs displaying the current solver state may not work properly (the EXITED tab will never be highlighted).
If you need to know when and why the solve() method terminated, OptimJ GUI provides a wrapper that sends the appropriate events. Here is how to use it:
LpSolve lp = ...;
LpSolveWrapper lpw = new LpSolveWrapper(lp);
LpSolveGUI.bind(lpw);
...
int status = lpw.solve(); // call lpw.solve() instead of lp.solve()
Starting in running state
By default, OptimJ GUI starts in paused state (you'll need to click 'Run' before anything happens). You can ask the GUI to start in running mode by specifying an additional parameter to the bind method:
Here are all versions of the bind method:
public static void bind(LpSolve lp, ViewersContainer viewers);
public static void bind(boolean paused, LpSolve lp);
public static void bind(boolean paused, LpSolve lp, ViewersContainer viewers);
public static void bind(LpSolveWrapper lpw);
public static void bind(LpSolveWrapper lpw, ViewersContainer viewers);
public static void bind(boolean paused, LpSolveWrapper lpw);
public static void bind(boolean paused, LpSolveWrapper lpw, ViewersContainer viewers);
Appendix
Table properties
The following image shows a table and its properties. Additionally, all cells have the following properties:
- texter: how to display the text.
- decorator: how to customize the label.
- creator: how to provide an arbitrary SWT Control.
Type hierarchy and properties
Below is an exhaustive list of all classes and fields relevant for using OptimJ GUI. Except for the classes generated as part of the model, they all belong to the package
com.ateji.optimj.gui.widgets.viewer
| ViewersContainer | the generic viewer container class | |||
| viewers : TableViewer[] | ||||
| DefaultViewersContainer | the viewer container generated by default by the OptimJ compiler | |||
| MyViewersContainer | viewer container customized by the user | |||
| TableViewer | a viewer displaying data in a 2-dimensional format | |||
| columnWidth : int | width of a column | |||
| name : String | name of the table | |||
| rowHeight : int | height of a row | |||
| xIndexHeight : int | height of an index cell | |||
| yIndexWidth : int | width of an index cell | |||
| xIndexes : Index[] | ordered list of the indexes that goes to x (top) | |||
| yIndexes: Index[] | ordered list of the indexes that goes to y (left) | |||
| TableViewer_myField | the default viewer generated by the OptimJ compiler for the field "myField" | |||
| element: ElementN | N is the number of indexes | |||
| index0: Index | ||||
| ... | ||||
| indexM: Index | M is N-1 | |||
| ElementN | used to display elements of an N-dimensional structure | |||
| texter: TexterN | ||||
| decorator : DecoratorN | ||||
| creator : CreatorN | ||||
| Index | used to display an index of an N-dimensional structure | |||
| texter: Texter1 | ||||
| decorator : Decorator1 | ||||
| creator : Creator1 | ||||
| TexterN<V1,...,Vn> | specifies the text to be displayed | |||
| String text(V1 v1,..., Vn vn) | ||||
| DecoratorN<V1,...,Vn> | decorates an SWT Label | |||
| void decorate(Label l, V1
v1,..., Vn vn) |
||||
| CreatorN<V1,...,Vn> | creates and decorates an arbitrary SWT control | |||
| Control create( Composite
parent) |
||||
| void decorate(Control c, V1 v1,..., Vn vn) |
||||
Customer quotes
With OptimJ you get the expressiveness of OPL™ with the integrability and flexibility of Ilog Concert™
-- the best of both worlds.
Luc Mercier,
Phd student,
Brown University.
Newsletter
Whitepapers
"The
economics of OptimJ a business case."
Optimization projects
that took weeks are now measured in days. Thisbusiness-oriented whitepaper
explains why.
"Object-OrientedModeling
with OptimJ."
OptimJ enables Object-Oriented Modeling, a radically new way to
expressyour optimization models. This technically-oriented
whitepaperdemonstrates this concept on an example.
