Quick Start: My First WebUI App

This section illustrates how to create quickly a first WebUI for a simple Transport Network (TransNet) application using a set of locations divided in two (disjoint) subsets of factories and distribution centers. Each factory has an amount of supply of a product and each distribution center has some demand for that product. Decisions have to be made on how much to transport from a factory to a distribution center in order to satisfy demand, while not exceeding the available supply. Unit transport costs are associated with any pair of factory and distribution center and the transport decisions aim to minimize the total transport cost for the entire network.

A first Web-based User Interface for such an AIMMS optimization application may look like in the following picture:

../_images/transnet-overview_v1.png

In order to build such a WebUI, one needs to add and configure step-by-step a number of widgets on the Home application page opened in a browser tab. Most widgets reference data of identifiers or run procedures present in the AIMMS model. The underlying mathematical optimization model used in this application is essentially the one described in the AIMMS tutorial for beginners.

Underlying model

In the sequel, some code snippets from the underlying AIMMS model show identifiers from the model declarations. The declarations for the network locations look as follows:

DeclarationSection Network_Locations {
    Set Locations {
        Index: l;
    }
    Parameter Latitude {
        IndexDomain: l;
    }
    Parameter Longitude {
        IndexDomain: l;
    }
    Parameter IsFactory {
        IndexDomain: l;
        Range: binary;
    }
    Parameter IsCenter {
        IndexDomain: l;
        Range: binary;
        Definition: 1 - IsFactory(l);
    }
}

Subsequently, the following model declarations concern the input data for the model:

DeclarationSection Input_Data {
    Set Factories {
        SubsetOf: Locations;
        Index: f;
        Definition: {
            { l | IsFactory(l) }
        }
    }
    Set Centers {
        SubsetOf: Locations;
        Index: c;
        Definition: {
            { l | IsCenter(l) }
        }
    }
    Parameter Supply {
        IndexDomain: f;
    }
    Parameter Demand {
        IndexDomain: c;
    }
    Parameter UnitCost {
        IndexDomain: (f,c);
    }
}

In the use case at hand, there are 3 factories (Hamburg, London, and Zurich) and 10 distribution centers (Amsterdam, Copenhagen, Dresden, Edinburgh, Frankfurt, Liege, Marseilles, Nantes, Paris, and Vienna). The input data may be initialized upon the application start-up by using some simple procedures. The Latitude and Longitude parameters, as well as the IsFactory binary indicator parameter may be initialized in a procedure by using eg, a composite table (or, alternatively, by reading in their values from an external source):

Procedure InitializeNetwork {
    Body: {
        COMPOSITE TABLE:
        l                Longitude      Latitude     IsFactory
        !-----------------------------------------------------
        Amsterdam          4.88           52.38
        Copenhagen        12.54           55.66
        Dresden           13.74           51.05
        Edinburgh         -3.19           55.95
        Frankfurt          8.71           50.17
        Hamburg            9.98           53.54           1
        Liege              5.58           50.63
        London            -0.16           51.50           1
        Marseilles         5.38           43.29
        Nantes            -1.55           47.22
        Paris              2.34           48.86
        Vienna            16.37           48.20
        Zurich             8.54           47.37           1     ;
    }
}

The Supply and Demand parameters may be initialized eg, by using some random draws from uniform distributions, while the parameter UnitCost may be initialized eg, based on the (straight) distance between two locations (see further below).

Creating an empty WebUI

One may start by opening the Home page of the WebUI application in the browser (see Create a WebUI section for details):

../_images/transnet-empty-start.png

Adding the widgets for input data

On this page one can add widgets for rendering the data of the input parameters Supply and Demand. For example, one can add a table widget named “SupplyData” with sizes of 2 columns and 2 rows, rendering the data of the parameter Supply. In the Widget Manager, pressing the “+” sign at the bottom opens the “Add widget” pop-up window, where the fields can be filled in as shown below, resulting in a widget like here on the right hand side:

../_images/transnet-joint-1_v3.png

The SupplyData widget can be further configured in its Settings window, for example by swapping indexes in the Pivot section or by typing a title in the Miscellaneous section, as illustrated below. After these steps, the SupplyData table should look like in the last picture on the right hand side below:

../_images/transnet-joint-2.png

In a very similar manner, one can undertake steps as above for adding and configuring a second table widget named “DemandData” with sizes of 3 columns and 2 rows, rendering the data of the parameter Demand. The two added table widgets should now look like in the following picture:

../_images/supply-demand-1_v1.png

Next, one can add a map widget with 4 columns and 3 rows in order to show the locations of the network. Before actually adding the widget, the following declarations should be made in the model:

DeclarationSection WebUI_Section {
    Set sLonLat {
        Index: iLonLat;
        Definition: data { 'Lon', 'Lat' };
    }
    Parameter MapCoordinates {
        IndexDomain: (l,iLonLat);
        Definition: {
            if ( iLonLat = 'Lon' )
                 then Longitude(l)
                 elseif (iLonLat = 'Lat' )
                         then Latitude(l)
            endif
        }
    }
}

Now the map widget can be added by using the “Add widget” window as before and then in the Setting window of the map widget the Map Contents and the Miscellaneous sections can be filled in as shown below, resulting in a widget as below on the right:

../_images/transnet-joint-3.png

Another table widget with 5 columns and 1 row may be added for showing the unit transport costs between a factory and a distribution center. The widget tile may be added in the same way as above, while the splitting of indexes of this parameter between the row area and the column area may be configured in the Pivot section of the Setting window as illustrated below:

../_images/transnet-joint-4.png

In the Miscellaneous section of Page Setting window, one can set a maximum number of 11 columns for the page as shown here:

../_images/transnet-pagesettings.png

After performing all the steps so far, the WebUI should look like in the following picture:

../_images/transnet-step3.png

Including widgets for optimization data

The mathematical optimization declarations in the AIMMS model look as follows:

DeclarationSection Optimization_Model {
    Variable Transport {
        IndexDomain: (f,c);
        Range: nonnegative;
    }
    Variable TransportCost {
        Range: free;
        Definition: sum[ (f,c) , UnitCost(f,c) * Transport(f,c) ];
    }
    Constraint SupplyRestricted {
        IndexDomain: f;
        Definition: sum[ c, Transport(f,c) ] <= Supply(f);
    }
    Constraint DemandRequired {
        IndexDomain: c;
        Definition: sum[ f, Transport(f,c) ] >= Demand(c);
    }
    MathematicalProgram TransportModel {
        Objective: TransportCost;
        Direction: minimize;
        Constraints: AllConstraints;
        Variables: AllVariables;
        Type: Automatic;
    }
}

Some additional declarations for the model output can be added as follows:

DeclarationSection Output_Data {
    Parameter IsTheModelSolved {
        Range: binary;
    }
    Set LP_ModelStatuses {
        SubsetOf: AllSolutionStates;
        Definition: data{ 'Optimal', 'Infeasible', 'Unbounded', 'SolverNotCalled' };
    }
    ElementParameter TransportModelStatus {
        Range: LP_ModelStatuses;
        Definition: {
            if IsTheModelSolved
               then TransportModel.ProgramStatus
               else 'SolverNotCalled'
            endif;
        }
    }
}

For output data one may add to the Home page in the WebUI a table widget with 2 columns and 2 rows (title Transport) showing the data of the Transport variable and a scalar widget with 2 columns and 1 row (tile Total Transport Costs) rendering the data of the variable TransportCosts. These Settings of two widgets may be configured as shown in these two pictures, respectively:

../_images/transnet-joint-5.png

The last two widgets have no data to be shown before the model is solved, so, after performing also the last steps, the WebUI should look as follows:

../_images/transnet-step4.png

Adding buttons

The procedures which initializes the input state before solving the model may be declared for example like here:

Procedure InitializeUnitCosts {
    Body: {
                UnitCost(f,c) := Round( Sqrt(Sqr(Latitude(f)-Latitude(c)) + Sqr(Longitude(f)-Longitude(c))), 2 );
    }
}

Procedure InitializeInput {
    Body: {
        option seed = 1234;

        Supply(f) := Round( Uniform(40,80), 2 );
        Demand(c) := Round( Uniform(10,20), 2 );

        InitializeUnitCosts;

        EmptyVariables;
    }
}

where the procedure on the last line above simply empties the output state:

Procedure EmptyVariables {
    Body: {

        empty Transport, TransportCost, IsTheModelSolved;
    }
}

On the WebUI page one can now add a button widget with 3 columns and 1 row (tile (RE-)INITIALIZE DATA) and configure it in the Action section of its Settings window in order to run upon click the procedure InitializeInput as illustrated below:

../_images/transnet-joint-6.png

In order to experiment using other unit transport costs than the initial ones, a procedure as the following may be used:

Procedure ModifyUnitCosts {
    Body: {

        InitializeUnitCosts;

        option seed = 5678;

        UnitCost(f,c) *= Uniform(0.3,2.5);

        EmptyVariables;
    }
}

Similarly as before, one can add another button widget with 2 columns and 1 row (title MODIFY UNIT COSTS) running upon click the last procedure mentioned above. After performing all the steps so far, the WebUI page should look like shown below:

../_images/transnet-step5.png

Solving the optimization model

Now it’s time to actually solve the optimization model and show the resulting output data in the WebUI. In developer model, solving the model normally requires a simple procedure as the following:

Procedure SolveModel {
    Body: {

        solve TransportModel;

        IsTheModelSolved := 1;

        !If the problem was not solved to optimality, make sure that the decision variables
        !are emptied, because they do correspond to actual solution.

        if ( TransportModelStatus <> 'Optimal' ) then
            empty Transport, TransportCost  ;
        endif ;
    }
}

However, in case the application is run on AIMMS PRO, the WebUI client needs to delegate the optimization procedure to the solver session running on the PRO server using a procedure as the following:

Procedure DelegateSolve {
    Body: {

        if pro::DelegateToServer( waitForCompletion : 1, completionCallback : 'pro::session::LoadResultsCallback' )
           then return 1;
        endif ;

        SolveModel;
    }
}

So, the overall solving procedure which addresses all situations (developer mode and deployment PRO mode) can be declared like shown below:

Procedure SolveProcedure {
    Body: {

        if ProjectDeveloperMode then

               SolveModel;

        elseif pro::GetPROEndPoint() then

                if pro::IsRunningAtServer then

                SolveModel;
                else
                DelegateSolve;
                endif;
        endif ;
    }
}

After making sure that the above declarations are present in the AIMMS model, one can add to the WebUI a new button widget with 4 columns and 1 row (title COMPUTE OPTIMAL TRANSPORT) running upon click the last procedure mention above, which actually solves the model. After pushing this button the output data is filled in for the Transport table and the Total Transport Costs scalar. However, the Transport amounts are not yet shown in the map widget. In order to achieve this, one needs to open the Settings window of the map widget and add the arcs in the Map Contents section of as follows:

../_images/networkmap-contents-2.png

So, after performing all the steps so far the WebUI should look as shown here:

../_images/transnet-step7.png

One may add to the WebUI a selectionbox widget with 2 columns and 1 row showing the value of the element parameter TransportModelStatus. After pushing the “MODIFY UNIT COSTS” button and then solving the model again by using the “COMPUTE OPTIMAL TRANSPORT” button, the WebUI overview should now show the new solution (with visible model status Optimal ) as in the following picture:

../_images/transnet-step8.png

Providing more structure to the UI

In order to indicate more clearly the input, the optimization, and the output parts in the WebUI, one may add 3 label widgets called InputLabel (contents INPUT), OptimizationLabel (contents OPTIMIZATION), and OutputLabel (contents OUTPUT), in this order. All labels can have 1 row, while the columns sizes may be 5 columns, 4 columns, and 2 columns, respectively. The label widgets may be moved up (by drag-and-drop) as the first 3 widgets in the Widgets Manager, in the same order, while the rest of the widgets may follow up by keeping their relative order in which they have been added. Furthermore, a new text widget may be also added as the very first widget in the Widget Manager in order to show a title / introduction section for the entire application (like the one shown in the first overview picture in the very beginning of this documentation section). Formatting the contents of such a text widget is self-explanatory in its Edit window by using the options on the top:

../_images/transnet-joint-8.png

The final Widget Manager showing all widgets and their relative order looks like shown here:

../_images/transnet-widgetmanager.png

After pushing again the data initialization button and the optimization button, one should retrieve the initial solution and the WebUI should look now as follows:

../_images/transnet-step10_v1.png

Adding What-If analysis

One can perform a “What-If” type of analysis by changing values for Supply, Demand, or UnitCosts, and subsequently re-solving the model. When input data changes, it may be handy to empty right away the output data resulted from the previous runs. This can be achieved by declaring procedures with names starting with “UponChange_” followed by the name of the identifier which incurs a change, similar to the ones below:

Procedure UponChange_Supply {
    Body: {
                EmptyVariables;
    }
}

Procedure UponChange_Demand {
    Body: {
                EmptyVariables;
    }
}

Procedure UponChange_UnitCost {
    Body: {
                EmptyVariables;
    }
}

One may change for example, the Demand value in Amsterdam to 50 and this action will also empty the widgets for output data and show in the model status that the solver has not been called yet:

../_images/transnet-whatif-supplya50_v2.png

After pushing again the “COMPUTE OPTIMAL TRANSPORT” button, the new solution is shown and the model status becomes Optimal again:

../_images/transnet-whatif-suppa50-opt.png

One may try and repeat the experiment for the scenario in which Demand value in Amsterdam would increase, for example, to 70. In this case the model becomes infeasible, because the total demand exceeds the total available supply. So, the output data stays empty and the model status is set to Infeasible:

../_images/transnet-whatif-supplya70-inf.png

Of course, one may experiment with several scenarios by altering (also simultaneously) several values in Supply, Demand, and UnitCosts, re-solving the model and observing the impact of the new input to the model status and the solution, in case the model is feasible.

Modifying widget types

Now let’s say we push again the data initialization button followed by the optimization button, such that we move back to the initial situation. However, one would like to visualize the inputs and outputs in a more graphical way. Some widgets allow to change type and show the data differently without defining a new widget for that. For example, in the Settings window of the table widgets for Supply or Demand, in the Change Type section the type may be changed, eg, to “bar chart”, while for Transport to, eg, “pie chart”, as shown below:

../_images/transnet-joint-7.png

The final result

These changes lead to a WebUI as the one illustrated in the very beginning of this documentation section. Moreover, other visualizations are possible as well. For example, if the type of the table for UnitCosts is changed to tree-map and the indexes are swapped in the order (c,f), then the final WebUI may look like in this picture:

../_images/transnet-overview-2.png

Hopefully this quick tour will give the reader a good understanding of how to start using the AIMMS WebUI. The next sections of this manual provide all the details required for building full-scope, professionally looking Web-based User Interfaces for the AIMMS applications.