Interactive Operation of the MAGNET Customer Agent components

Prior to the Agents 2001 demo, the default mode of operation was non-interactive. The Agent components were designed originally to support bid-evaluation experiments, using XAutoCustomer as the top-level class. The demo requires interactive control of individual components. Several Mantis bug reports relate to this issue, including 6, 8, 16, 57, 59, 60, 63.

The first step in this transformation was to implement the bid process timeline in the time map, and to use PropertyChangeEvents to notify interested parties about state changes, incoming bids, etc. The second step is to deconstruct the BidManager to allow for independent control of its elements, since this is where most of the automation is happening.

Currently, you start up a BidManager by creating one with a SessionStatus instance (which has as a parent a PlanStatus instance containing your initial TaskBidPlan) and a Properties instance (presumably this is the one from MagnetProperties, but you are free to create your own). It then creates a BidScheduler and a TaskDurationAdjuster, for later use. The specific subclasses used are controled by property settings. You then start up the BidManager by calling its handleBids method, and it fires off a thread that does the following:

  1. Uses its BidScheduler to generate an agenda. Currently the only thing that gets put on the agenda is instances of BidCommand. You can get multiple entries on the agenda if you use a BidScheduler that breaks down the bidding process into multiple phases. Eventually, you could also get multiple instances if you were bidding in multiple markets.
  2. Sends some statistics to the log files.
  3. Runs each BidCommand in turn, until the agenda is empty. Running a bid command (see the method doBidCommand) involves these steps:
    1. Sets the state of each task in the bid command to TaskStatus.OFFERED.
    2. Sets the time windows on the tasks to be submitted with the RFQ, using a PlanTWAdjuster, which wraps and controls the TaskDurationAdjuster. The original SubtaskRequest instances are modified in this step.
    3. Creates a new TaskPlan, containing only the tasks to be bid out in the current cycle.
    4. Composes an RFQ, containing the reconstructed TaskPlan, and timing info from the BidCommand.
    5. Creates an RFQStatus instance to monitor and control the bidding process itself.
    6. Registers for state change events emitted by the RFQStatus instance. The listener calls the summarizeBids method when the state changes to RFQStatus.BIDS_ACQUIRED.
    7. Creates a new BidEvaluator instance and initializes it. The BidEvaluator will register with the RFQStatus instance for new bids, and for the BIDS_ACQUIRED state change. When the state change occurs, the BidEvaluator will proceed to evaluate the bids, using the settings available in MagnetProperties.
    8. Invokes the acquireBids() method on the RFQStatus instance to send out the RFQ and gather in the bids. The RFQStatus instance issues NewBid events when new bids arrive, and State events when the bidding closes, and again when all bids have arrived.
    9. Waits for the BidEvaluator to finish its work by calling its getResult() method, which will block until the BidEvaluator has finished, and then return the search result.
    10. If the BidEvaluator returned a non-null result, sets each of the tasks in the RFQ to the TaskStatus.PENDING state, which indicates that an award decision has been made.
  4. Reports the results of the whole process (the result is embodied in the final TaskBidPlan).

This scheme works quite well for running bid-evaluation experiments. However, interactive operation needs to be able to access and control at least some of these steps independently. With the background given above, let's examine the three relevant Use Cases from the demo requirements documentation.

Compose RFQ
This is basically steps 3.1-3.4 above. In interactive mode, you have to create a BidManager, but don't start it. Then create a BidCommand and call the BidManager's composeRFQ(BidCommand) method, which returns the RFQ. Time window settings will be controlled by the Properties object you hand to the BidManager when you create it. Here's the basic process:
  1. The methods of BidManager, including composeRFQ() work against a TaskBidPlan, rather than directly against the TaskPlan. It gets the TBP from the PlanStatus, which creates it during initialization from the TaskPlan you supply. This is because the embedded TaskBid instances contain the necessary fields for the Critical Path (CPM) algorithm to work against.
  2. Normally, unless there's some particular reason to constrain the start or end time of a task, the early start and late finish times of the SubtaskRequest objects are null at this point. They will be computed during this process.
  3. All tasks specified in the BidCommand are put into the OFFERED state. Once in this state, they should no longer be editable.
  4. A TaskDurationAdjuster (TDA) was created by the BidManager at init time, as specified above. The specific class of TDA is controlled by "TDA.Type" property.
  5. A new PlanTWAdjuster is created. Its setTimeWindows method is called with the current TaskBidPlan, the plan start date, the plan slack ratio, and the selected TDA. This method uses a version of the CPM algorithm (other algorithms are under investigation) to update the early start and late finish times in the underlying SubtaskRequest objects. This is done in the following steps:
    • The makespan of the plan is computed as the difference between the earliest possible (expected) finish time for the entire plan and the plan start date. The plan early finish time is computed as follows:
      1. For root tasks (those with no predecessors), the early start time is the later of the plan start time and (if the SubtaskRequest early start is non-null) the early start time of the SubtaskRequest.
      2. The early finish time of any task is the early start + expected duration.
      3. For all non-root tasks, the early start time is the later of the latest early finish time among its predecessors, or (if non-null) the early start time of the SubtaskRequest.
      4. The early finish time for the plan is the latest of all computed early finish times for tasks.
    • The expected duration of each task is multiplied by the value of the property "TDA.DurationMult". The default value is 1.0.
    • The plan deadline is computed as
            plan_deadline = plan_start + (makespan * plan_slack)
          
      where plan_slack is the value of the "PlanSlack" property.
    • New early start times are computed for each task as described above, using the new durations.
    • Late finish times are computed for each task in a similar fashion to the early start computation, except that in this case it's leaf tasks that must end at least as early as the plan deadline. As before, the final late finish time of a task is the earlier of the computed late finish time and (if non-null) the task deadline of the individual task.
    • All SubtaskRequests that were listed in the original BidCommand have their early start and late finish times updated with the computed values. Note that if those times were non-null to begin with, early start times can become later, and late finish times can get earlier. Because of that, it's possible that poor choices of those times prior to composing the RFQ can cause infeasibility.

Currently, this process neither honors nor updates the planDeadline property of the PlanStatus. This is a bug (#0000096).

After the time windows are computed, an RFQ instance is created and filled in. The tasks are the ones in the OFFERED state as specified in the BidCommane, and the RFQ timings (bid deadline, early consider, early offer) are also taken from the BidCommand. The Session ID and Agent ID are also set in the RFQ (but see bugs #0000095 and #0000097).

Once the RFQ is composed, it can be viewed and further edited, before passing it off to the next step. Note that you might want to wait to set the RFQ time factors until that step (or set them again), even though they are getting set in composeRFQ.

Manage Bidding Process
This is steps 3.5, 3.6, and 3.8 above. Steps 3.5 and 3.6 are accomplished by calling BidManager.createRFQStatus(RFQ). You get back the RFQStatus instance, which you can then use to register for events, issue the RFQ, and retrieve bids. Step 3.8 is accomplished by calling acquireBids() on the RFQStatus instance.
Evaluate Bids
This is steps 3.7, 3.9, and 3.10 above. You can simply call the same methods the BidManager would call to run the BidEvaluator. If you do this after the RFQStatus instance has already reached the BIDS_ACQUIRED state, then no bids will be sent to the BidEvaluator by the RFQStatus, and the state change event that causes the BidEvaluator to run its search will not occur. That means you have to call addBid(Bid) on the BidEvaluator for each bid you want it to consider, and you have to call its evaluateBids() method to run the search. You can complete the BidManager's job by calling its setCurrentPlan(TaskBidPlan) method with the final TaskBidPlan you get from your evaluation process.

For more control of the evaluation process, the BidEvaluator now offers PropertyChangeEvents and an alternative mode of activation. Here's the process:

  1. Create the BidEvaluator and initialize it. Initialization consists of
          evaluator.setProperties(properties);
          evaluator.setSessionId(sessionId);
          evaluator.setPlanStatus(planStatus);
          evaluator.setRFQStatus(rfqStatus);
        
    where that last step will cause the new evaluator to register with the RFQStatus object for new bid and state change events. The search type(s) is set in the properties object. Search types and other property settings can be found here under the "Evaluation Parameters" and "Search Control" headings. If multiple search types are specified they will (currently) be run serially. The evaluator is now ready to run a search, but the search hasn't been started.
  2. For each bid you wish to have considered in the evaluation, call evaluator.addBid(bid).
  3. Register with the BidEvaluator for property change events. You do this by calling its addListener(PropertyChangeListener) method. The events will be of class BidEvaluatorEvent. Events will carry one of the following property names:
    BidEvaluator.NEW_RESULT
    Emitted when the search turns up a new "best result". The newValue property of the event will contain the TaskBidPlan that represents the new result.
    BidEvaluator.SEARCH_COMPLETE
    Emitted when a search has completed normally. The TaskBidPlan representing the search result will be included as the newValue of the event.
    BidEvaluator.SEARCH_ABORTED
    Emitted when a search has been aborted by calling abortSearch on the BidEvaluator. The TaskBidPlan representing the best result found so far will be included as the newValue of the event.
  4. Call the startSearch() method on the BidEvaluator. This will start the specified search(s) in a new thread. Events will be thrown as the search progresses.
  5. If you wish to apply a time limit to the search, you can create a new TMEntry with an action that calls the BidEvaluator's abortSearch() method. Then add the new TMEntry to the TimeMap of the current PlanStatus instance. You do that by calling the addEntry(TMEntry) method. Just remember that the times represented by TMEntry instances are scaled in accordance with the current AgentTime settings. If you don't want to mess with scaled time, you can use an AbsoluteDelay instance instead, but then you won't be using the TimeMap.


Copyright: © 1999, 2000 by the Regents of the University of Minnesota
Department of Computer Science and Engineering. All rights reserved.
The University of Minnesota is an equal opportunity educator and employer.

$Id: interactive.html,v 1.4.2.1 2002/05/08 00:27:51 jcollins Exp $