A Flexible Java Representation for Uncertainty in

5 downloads 0 Views 411KB Size Report
the mathematics of uncertainty is naturally polymorphic (random variables can map .... programs use a static characterization of uncertainty where the statistical .... supports multiple inheritance of interface types but not of class types, so that a ...
A Flexible Java Representation for Uncertainty in Online Operations-Research Models Joel A. Shapiroy  Warren B. Powell  David Bernstein Department of Operations Research and Financial Engineering, Princeton University, Princeton, NJ 08544, USA Department of Operations Research and Financial Engineering, Princeton University, Princeton, NJ 08544, USA Department of Computer Science, James Madison University, Harrisonburg, VA 22807, USA [email protected]  [email protected]  [email protected] y Now at: i2 Technologies, Inc., 5 Cambridge Center, Cambridge, MA 02142.

Online OR models have been the subject of increased attention in recent years with the rapid expansion of the Internet. Although much has been written about the implementation, as well as the formal analysis of online models, little has been said about how to handle uncertainty in an online setting. In particular, the dynamic nature of uncertainty that is so characteristic of online models, where estimates and distributions evolve in parallel with the state of the model, has been largely ignored. In this paper, we present a new representation for uncertainty in online models. This representation is object-oriented and, as such, provides several important softwareengineering advantages over traditional representations for uncertainty. Moreover, by using the event listener paradigm it provides an explicit mechanism for handling dynamic uncertainty in an elegant and extensible manner. A series of computational experiments demonstrates that there is no signi cant overhead to our representation when compared to traditional representations on a realistic application and, in some cases, our representation can be noticeably faster. (Software; Philosophy of Modeling; Simulation; Programming: Stochastic)

1. Introduction The explosive growth of the Internet, and computer power in general, has produced a surge in the implementation of online models in operations research. An online operations-research model can be loosely de ned as any model used for ongoing, real- or near-real-time computer-based decision support. Online models are solved by online algorithms. The use of online algorithms dates back to the early days of computer operating systems, when online job-scheduling algorithms were rst constructed to schedule the CPU dynamically to execute an uncertain stream of programs (also called \jobs"). These early algorithms were not user-accessible and instead ran \under the hood" as background jobs. A user could only see the results of the algorithm implicitly through the relative swiftness with which his job executed. In contrast, more recent applications of online 1

algorithms support explicit user access to results, often for the purposes of planning or forecasting some external system. Examples of online models abound. For instance, Perros and Elsayed (1996) analyze a variety of call admission control models for telecommunications providers that are used for online assignment of communications bandwidth to data streams (e.g., real-time video conferencing, voice trac and multicast broadcasting like a recent Rolling Stones concert). The nancial industry has long been an advocate of online models, for instance for portfolio risk management systems as in Linsmeier and Pearson (1996). Other examples include load forecasting for electric utilities (Paarmann and Najar 1995, Sharaf and Tjing 1995), process-scheduling in chemical re nement (Ishii and Muraki 1996), lead-time quotation in manufacturing (Keskinocak et al. 1998) and the assignment of drivers to loaded freight trailers in truckload trucking (Powell 1996). As Bhargava, Krishnan and Muller point out (1997a, 1997b), the advent of electronic commerce and supply-chain management as business practices should ensure continued growth in the use of online models. Formal analysis of online algorithms has been divided into two camps: exible computation and computational complexity. Flexible computation is a term rst coined by Horvitz (1987) to describe the need for online algorithms to trade o gracefully solution quality with the allocation of limited resources (e.g., CPU time and memory). Flexible computation can also involve meta-reasoning, known as re ection (Horvitz et al. 1989), whereby a portion of computational resources are dedicated to consider the utility of alternate algorithmic strategies (e.g., \Which algorithm in my arsenal is best-suited to solve the problem at hand given my current deadlines and challenges?"). A second branch of exible computation is known as continual computation. As modeled by Horvitz (1997), continual computation extends the de nition of an online model instance from a single set of problem data to a continuing dynamic sequence of problems. The goal of continual computation is to use periods of time between problems in a sequence, traditionally viewed as idle time, to develop solutions or partial solutions to future problems in the sequence. Horvitz (1998) presents a topical example of continual computation in the context of document prefetching on the Internet. While a user browses document i, her browser's prefetch algorithm can download into cache any related documents (e.g., from hyperlinks on i), which may reduce latencies in retrieving future content. Analysis of the computational complexity of online algorithms has been studied by Sleator and Tarjan (1985). They proposed a technique, later termed competitive analysis by Karlin et al. (1988), that measures the e ectiveness of an online algorithm by comparing its performance to an optimal oine algorithm. An optimal oine algorithm knows everything about the problem facing the model (the information set) in advance, and can thus make the lowest-cost or highest-pro t set of decisions. Reasons why an online algorithm may not compete with an oine counterpart include (Albers 1997):

 Limited computational resources: Online algorithms often run in smaller memory spaces on lower-powered computers and typically face strict time limitations.

 Extreme and dynamic uncertainty: Online algorithms often depend on far more un2

certain data than do oine algorithms. In addition, the uncertainty faced by an online algorithm often changes rapidly as the algorithm iterates (e.g., new estimates arrive, parameters of distributions change) so that the characterization of the uncertainty faced by the model is dynamic. Oine algorithms typically face a static characterization of uncertainty where new estimates and updated distribution parameters do not arrive during the execution of the model. Naturally, most traditional representations assume a static characterization of uncertainty. If new information about the uncertainty does become available (via an information update), an oine model is usually run again to generate an updated solution. For instance, simulation modeling environments handle random quantities (e.g., the occurrence time  of some activity) by sampling a single value from a pseudo-random number generator (PRNG). An event is then scheduled to occur at time  by placing an event notice on the future event list. When the characterization of uncertainty is indeed static, this is a ne approach, and has been rendered very ecient through numerous studies on how best to structure the future event list (see McCormack and Sargent 1981 for a summary). Unfortunately, this method of handling uncertainty does not extend elegantly to models with dynamic uncertainty. For instance, if a new estimate ^ of  becomes available, the event scheduled for time  must be removed from the list and re-scheduled at time ^. When the list is even moderately large, this approach can prove quite slow, and from a software-engineering perspective it is somewhat sloppy. Some work-arounds have been attempted. The activity-scanning and threephase approaches (Banks et al. 1996) handle dynamic uncertainty by maintaining a list of so-called conditional events. Whereas events that can be de nitely scheduled to occur at some time  are unconditional events (they are bound to happen at time  ), the occurrence time of conditional events cannot be captured as a single number. Instead, conditional events occur when some list of conditions is satis ed (e.g., that some other event(s) have occurred). Simulation environments maintain a list of conditional events that is repeatedly scanned at each time until a scan of the list is made where no more conditional events signal that they are ready to occur. Then the clock is advanced to the next time of occurrence of an unconditional event, after whose occurrence scanning for conditional events begins again. As Banks, Carson and Nelson point out (Banks et al. 1996), \repeated scanning to determine whether an activity can begin results in slow runtime on computers." Other formal approaches for handling uncertainty have been proposed for algebraic modeling languages such as AMPL (Fourer et al. 1993). Algebraic modeling languages make a signi cant contribution by mapping the mathematics of optimization models very closely to a compact software representation. Extensions to accommodate uncertainty formally in AMPL in a more exible and natural manner generally revolve around scenario linear programming, as in Gassmann and Ireland (1995, 1996). Kall and Mayer (1996) have made similar extensions to GAMS in their system SLP-IOR. Although innovative, these extensions make no special provision for the dynamic nature of uncertainty in online models (e.g., that new scenarios may evolve as the simplex iterates). 3

Since the value of including uncertainty in operations-research models (both online and oine) has long been recognized, we do not weigh the advantages of deterministic versus stochastic models (see Birge 1997). Instead, the objective of this paper is to propose a new software representation that enables online models to incorporate uncertainty in a very exible and ecient manner. Our representation is structured to provide three key advantages over traditional representations: 1. A functional representation: Random variables are modeled as a function to capture their behavior instead of their values. This ts more closely with the mathematical de nition of a random variable x : 7! . The use of the Java language interface mechanism supports our functional representation in an elegant and well-understood manner. IR

2. A dynamic representation: Updates to estimates of uncertain quantities as well as changes in parameters or distributions are handled explicitly by the event listener paradigm. This can handle such information updates more eciently than traditional representations for the pattern of information updates typically seen by most online models. The event listener paradigm is well-understood and widely used by object-oriented programmers. 3. A exible representation: The value of a random variable is modeled as an object and not a primitive data type like double. This is a more extensible and object-oriented approach and proves useful for many models that do not naturally use real-valued random variables. Since the mathematics of uncertainty is naturally polymorphic (random variables can map to any space, provided it's measurable) it only makes sense to build data structures that capitalize on this polymorphism. It should be clear that what is needed is a new representation for uncertainty in operations-research software. We use modern software-engineering methods to create the foundation for a robust modeling toolkit for stochastic online models. The representation that we describe has been fully implemented and is available for download and experimental use (see Section 5). More important than the possible improvement in execution speed that it supports, our representation can signi cantly improve coding speed, the speed with which a usable system can be constructed and maintained. Since it is estimated (Bell et al. 1992) that fully 67% of the lifecycle cost of a software project is spent on code maintenance and extension to meet changing user needs (totaling 2% of the United States' GNP; see Bell et al. 1992), the increased coding speed (maintainability and extensibility) of our software representation is a major contribution to the modeling, solving and coding of stochastic online OR models. In the next section, we contrast with speci c existing representations to point out that the new representation must be functional, orthogonal and support ecient updates to estimators. Then, in Section 3, we introduce just such a new representation, using an object-oriented design for exibility and the event listener paradigm to support ecient updates. The discussion moves in Section 4 to issues of probabilistic modeling, including measurability and the appropriateness of operations on random variables with di erent range-spaces. Section 5 outlines the implementation status of the 4

representation and instructs users on how to download the code for experimental use. Section 6 then derives theoretical measures of the computational complexity of our representation for uncertainty as well as traditional representations. Because these theoretical measures are dicult to compare analytically, Section 7 reports the results of computational experiments that demonstrate their values empirically. We consider two online applications in eet management. The rst uses a simple threshold dispatch algorithm to handle dynamic uncertainty external to the application. The second uses a full auction algorithm to handle dynamic uncertainty internal to the application.

2. Motivation In basic probability theory, one learns that a random variable, for instance x, is a function:

x : 7! F :

(1)

The set is the collection of possible outcomes !, to each of which corresponds a value, x(!) 2 F . We call (1) the functional representation of uncertainty. In contrast, computer programmers have traditionally handled random variables (r.v.'s), like x, as software variables. They use what we call the value representation: struct Data { double x; /* C-language representation of r.v. x */ };

In so doing, they ignore an important distinction between the function x : 7! F and its values, elements x(!) of the set F . In this paper we introduce a truly functional representation of uncertainty. Computer programmers have chosen the value representation largely as a matter of convenience. Most procedural computer languages used in operations research do not support a functional style of programming. Because functions are not rst-class values, that is, values that can be passed to and returned from other functions, it is dicult to implement a software representation of (1). For instance, in FORTRAN, one might model the random variable x as a simple function: DOUBLE PRECISION FUNCTION X() X = . . . RETURN END

This approach is problematic because it pollutes the global namespace with a new function identi er for each new random variable. For instance, extending it to accommodate a collection of random variables fx1 ; x2 ; : : :g would require: DOUBLE PRECISION FUNCTION X1() X1 = . . . RETURN END

DOUBLE PRECISION FUNCTION X2() X2 = . . . RETURN END

. . .

Consequently, the programmer must remember in distant functions and subroutines (i.e., those in separate modules/ les) what names he has assigned to functions representing his random variables 5

in unrelated sections of code. Because primitive data types, like double (e.g., Data.x), are rstclass values in virtually all languages, they can be passed about as formal parameters, thus avoiding global namespace pollution. This, in part, explains the common use of the value representation. A slightly di erent representation for uncertainty could be achieved in a language like C that treats pointers to functions as rst-class values (see Kernighan and Ritchie 1988 for details on pointers to functions in C). For instance, the syntax typedef double (*ptrRV)(void);

creates a type, ptrRV, which can store a pointer to a function taking no arguments and returning a double. Writing ptrRV ranvar;

declares a software variable ranvar that can hold any real-valued random variable that is de ned in the following fashion: /* Define a specific random variable x1 */ double x1(void) { . . . /* return a double representing x1(omega) */ }

Assigning a speci c random variable to ranvar is straightforward: ranvar = x1; /* The name of a function is a pointer to that function */

Now, using ranvar in an expression like (*ranvar)()

has the same e ect as an invocation of our FORTRAN function X1. In C, pointers to functions can be used as formal parameters to another function, as in double add(ptrRV x, ptrRV y) { return (*x)() + (*y)(); }

and also returned from functions, as in int orig = . . .; int dest = . . .; //Get the r.v. representing the travel time from orig to dest ptrRV travelTime = getTravelTime(orig, dest); . . . ptrRV getTravelTime(int orig, int dest) { ptrRV toReturn; /* Look up random travel time */ toReturn = . . .; return toReturn; }

6

Unfortunately, although this representation is functional, it is not orthogonal, that is, it does not readily support di erent numbers of random variables (i.e., in the argument list of add) or di erent types of random variables (i.e., in the return type of getTravelTime). Orthogonal designs allow the number or type of operands to be varied independently from the type of operation being performed. Examples of orthogonal operations in a language like C include array construction (number orthogonal, because any number of operands can be stored in the array) and numerical addition (combination orthogonal, because any type of numerical operands can be used through type coercion or promotion). Orthogonal designs are considered attractive by software engineers because they lead to simpler code, with fewer exceptions or special cases to remember (see Pratt and Zelkowitz 1996). We point out in Section 3.5 how our new representation is both number and combination orthogonal. A second motivation for using the value representation of uncertainty, besides its convenience in non-functional languages, is the timing context with which values become realized (i.e., when the outcome ! 2 determining the value x(!) of x is xed). Most traditional operations-research programs use a static characterization of uncertainty where the statistical estimators x^ of a random variable x are xed in advance. Because x^ does not evolve during a single run (with the exception of re-sampling between iterations), it is sucient to sample the value of x but once, xing ! and x(!) in the process. Suppose that the programmer is careful not to violate the rules of nonanticipativity, that is, he does not use future information before it is really known (in technical terms, the programmer's code must be adapted to the ltration representing the time history of relevant information; see Birge and Louveaux 1997). In such cases, a structure such as struct Data { double x; double xEst; };

/* realized value, x(omega) */ /* estimated value, xhat */

suces to represent both x and x^. However, in an online model, for instance a model running in real-time over the Internet, uncertainty is more often dynamic. Estimators x^ evolve over time, even within a single iteration. Consequently, x^ cannot be counted on as a single constant value. Accessing x^ from the eld Data.xEst may lead to runtime bugs or inconsistencies if the value of the eld is updated over time. We would do better to fall back to our functional representation: struct Data { ptrRV x; ptrRV xEst; };

/* ptr to function returning realized value, x(omega) */ /* ptr to function returning estimated value, xhat */

Because the programmer is forced to access the value of x^ through a function call, there is no implicit guarantee of immutability on xEst and updates can take place without fear of inconsistencies in value from one use to the next. However, adapting oine code to this new data structure would require a major rewrite of argument signatures, for instance in our add function. An alternative is to use a lookup table approach, for instance a hash table that maps a particular random variable pointer ptrRV x to its corresponding estimate pointer ptrRV xEst. Unfortunately, this solution 7

o ers poor locality of reference, added code complexity, and added runtime complexity. In all cases, it is the programmer's responsibility to poll Data.xEst continually for changes, and this can lead to clumsy code with many implementation dependencies. Finally, neither the value representation nor the functional representation is orthogonal, in that neither readily supports di erent forms of estimates. Di erent invocations of the same code may need both a point estimate x^, storable in the primitive type double, and an interval estimate x^, storable in a structure such as struct ConfidenceInterval { double lower; double upper; double confidenceLevel; };

Only a major code rewrite could adapt a traditional representation from a point estimator to such an interval estimator.

3. A New Representation Figure 1 gives a brief overview of the representation we propose. We explain each of the types Outcome: Information: ^ - Protocol for x(ω) and x. - Enforces nonanticipativity. - Event-based updates.

PrimitiveOutcome:

-The r.v. x.

CompositeOutcome:

- Univariate r.v. x. - User defines map x : Ω F.

- Multivariate r.v. x = (x 1 , . . . ,x n ). - Function of r.v. f(x 1 , . . . ,x n ).

Legend: class

interface

uses

extends

Figure 1: The Type Hierarchy for the Uncertainty Representation indicated in the gure in sections 3.1{3.5 that follow. All code examples that follow are in Java.

3.1. The Information Type The Information interface is the foundation of our representation. It de nes the functional representation of a random variable: 8

public interface Information { Outcome getOutcome(Object Index); }

For instance, x : 7! F could be implemented as: class x implements Information { public Outcome getOutcome(Object Index) { //details omitted . . . } }

The Information interface does nothing other than identify implementing classes as random variables. For instance, in a given class, elds of type Information are easily identi able as random variables even when passed to a distant function. Random variables stored in primitive types are not: //CASE 1: r.v. as primitive type class Data { double x; //r.v. double y; //deterministic }

//CASE 2: r.v. as Information type class Data { Information x; //r.v. double y; //deterministic }

class Other { //which args (if any) are r.v.? double add(double lhs, double rhs) { //details omitted . . . } }

class Other { //r.v. args are obvious double add(Information lhs, double rhs) { //details omitted . . . } }

The Outcome object returned by the method Information.getOutcome serves as a placeholder from which to obtain the realized value x(!) and estimates x^ of the random variable x implementing the Information interface. Just how this Outcome object is produced is implementationdependent. For simple random variables, the index argument to Information.getOutcome can be null. Stochastic processes (xt )t2T can be handled as a single Information object by passing t 2 T as index. As a matter of object-oriented design, designing Information as an interface rather than as a class makes it a speci cation of pure functional behavior without reference to an implementation. This ts well with the functional de nition of a random variable x : 7! F . Moreover, Java supports multiple inheritance of interface types but not of class types, so that a programmer's class can be of type Information while simultaneously implementing other interface types and extending a class. Although our examples up to this point have focused on univariate random variables, the representation is also designed to handle multivariate random variables. Sections 3.4 and 3.5 explain the di erences between the univariate and multivariate cases.

3.2. The Outcome Type Consider a random variable x : 7! F . While x^, an estimate of the value of x, should always be available, x(!), the actual value of x, can only be known when x is realized (i.e., ! is xed in 9

). We can think of objects of type Outcome as elements of that de ne a software protocol for operations to access x(!) and x^ in a manner that respects the measurability of x. To perform such operations on x one obtains an Outcome object from x as in: Information x = . . .; Outcome o = x.getOutcome(null);

This produces an object of the following type: public abstract class Outcome { //Return true if realized (omega fixed in Omega) and x(omega) is known, else false public abstract boolean isRealized(); //Return x(omega) if realized, else throw ValueUnknownException public abstract Object getValue() throws ValueUnknownException; //Return xhat public abstract Object getEstimatedValue(); }

Outcome is known as

an abstract class because it declares methods with the keyword abstract. For these methods, Outcome declares a behavioral contract in the form of method arguments, return types and exceptions that can be thrown, but leaves the method implementation (i.e., method body) to concrete subclasses. We explain in Section 3.4 how to implement the abstract methods for univariate random variables and in Section 3.5 how to implement them for multivariate random variables. The getValue method enforces nonanticipativity through its ability to throw checked exceptions of type ValueUnknownException, preventing client code from accessing the realized value x(!) until conditions of measurability permit (i.e., ! xed in , isRealized() == true). This is a subtle, but important point, which we explain in greater detail in Section 4.1. The use of an Outcome object is simple: Information x = . . .; //x : Omega -> R Outcome o = x.getOutcome(null); Double xval; try { xval = (Double)o.getValue(); } catch (ValueUnknownException e) { xval = (Double)o.getEstimatedValue(); }

The Outcome throws a ValueUnknownException if it is not yet realized (i.e., the identity of ! is unknown). In this case, the client code handles the exception by using the estimated value instead. This leads to a pleasant code style where the usage of random variables is clearly identi able by blocks like: try { ... } catch (ValueUnknownException e) { ... }

10

In addition, the need to handle the exception forces the programmer to consider both cases explicitly, where x is known and x(!) can be used, and where x is unknown and x^ must be used. The Outcome type is not restricted to random variables with scalar values (e.g., real-valued, those of type Double). Because the method Outcome.getValue returns a reference to type Object, it can return a reference to any type of abstract data structure (class or interface in Java). Object is the root of the Java type hierarchy and can store any reference polymorphically, for instance a reference to a value in n as an n ? dimensional array: IR

Information x = . . .; //x : Omega -> R^n Outcome o = x.getOutcome(null); Double[] xval; //Reference to n-dimensional array try { xval = (Double[])o.getValue(); //cast Object to n-dimensional array } catch (ValueUnknownException e) { xval = (Double[])o.getEstimatedValue(); //cast Object to n-dimensional array }

Even more exotic random variables can be handled, for instance those with non-numerical ranges (e.g., character strings for encryption applications or colors for a Kaleidoscope screen-saver). Although such non-numerical random variables may not easily accommodate numerical operations such as measure and expectation, they are nonetheless interesting from a software perspective.

3.3. The Event Listener Paradigm When the random variable x nally becomes realized, or a new estimate becomes available, interested objects can be noti ed by the event listener paradigm, as indicated in Figure 2 (see Englander 1997 for a brief review). The event source is the Outcome object of the random variable from a r.v. x

Event Source:

Register Event Listener

Outcome

Event Object x(ω) or x^

Event Listener:

Fire Event

OutcomeListener m algorith e.g. an

Figure 2: The Event Listener Paradigm (as in Englander 1997)

x. Each listener must implement the following interface: public interface OutcomeListener extends EventListener { void handleRealization(EventObject event); void handleEstimateUpdate(EventObject event); }

11

The Java language de nes the EventObject and EventListener types in its standard java.util package. We use the EventObject directly with an Outcome object, obtained from the random variable x, stored as the object's source eld. An Outcome object corresponding to the random variable x maintains a list of objects that are interested in x(!) and x^: public abstract class Outcome { . . . //NEW CODE BELOW: private java.util.Vector listeners; //list of interested objects public Outcome() { listeners = new java.util.Vector(); //Initially empty } //Used to register listeners (add them to the list) public void addOutcomeListener(OutcomeListener l) { listeners.addElement(l); } }

Each object in the list must implement the OutcomeListener interface: class SomeListener implements OutcomeListener { public void handleRealization(EventObject event) { Outcome o = (Outcome)event.getSource(); //Do something with x(omega) obtained from o.getValue() . . . } public void handleEstimateUpdate(EventObject event) { Outcome o = (Outcome)event.getSource() //Do something with xhat obtained from o.getEstimatedValue() . . . } }

Implementing OutcomeListener allows an interested object to signal its interest in the random variable x by registering itself with the Outcome object generated by x: Information x = . . .; //r.v. x Outcome o = x.getOucome(null); SomeListener l = new SomeListener(); //Object interested in x(omega) and xhat //At this point, x(omega) and xhat updates will not be communicated to l //Register l as an interested object with x o.addOutcomeListener(l); //Hence forward, x(omega) and xhat updates will be communicated to l

If every listener monitored every event generated in the program, there might be signi cant event trac and ineciencies. The registration mechanism that we have outlined allows a speci c event listener (an object of type OutcomeListener) to be tied to a speci c event source (an object of type Outcome). Consequently, an event listener sees only those events for the random variables in which it is interested, that is, those random variables with which it has explicitly registered itself by invoking the addOutcomeListener method. If a listener loses interest in the events generated by a speci c event source, it can \un-register" itself with that Outcome object by its simple removeOutcomeListener method, which we do not detail here. 12

When a new estimate x^ becomes available, the Outcome object of x res an EventObject to each listener registered with it by calling the listener's handleEstimateUpdate method: public abstract class Outcome { . . . //NEW CODE BELOW: //Invoked by subclass to signal new estimate xhat protected final void notifyEstimate() { EventObject e = new EventObject(this); for (int i = 0; i < listeners.size(); i++) ((OutcomeListener)listeners.elementAt(i)).handleEstimateUpdate(e); } }

It is the responsibility of a concrete subclass to invoke the notifyEstimate method, because only the subclass can know when a new estimate becomes available. In practice this may be signaled by another object, for instance a stock ticker, or the output of an online forecasting program. We demonstrate in Section 3.4 the use of the notifyEstimate method for univariate random variables, and in Section 3.5, its use for multivariate random variables. Code complexity increases marginally with the event listener paradigm. For instance, code inside an object l interested in the random variable x //Inside object l } catch (ValueUnknownException e) { xval = (Double)o.getEstimatedValue(); }

becomes //Inside object l } catch (ValueUnknownException e) { xval = (Double)o.getEstimatedValue(); o.addOutcomeListener(this); //this is the self-referential reference to //l available inside any method of l }

where l now implements the OutcomeListener interface. With the rst catch block, client code would have to poll the Outcome object for updates, while in the second it is automatically noti ed. Polling can be very time-consuming in some cases, and in these cases the \push-logic" of the event listener paradigm leads to faster code. An application of this might be a GUI that updates a display given the new update, or an auction algorithm that rebids a task given the new update. Moreover, the event listener paradigm encourages programmers to separate the generation of uncertainty (Outcome objects as event sources) from the use of that uncertainty (OutcomeListener objects as event listeners). Traditional representations for uncertainty, being more procedural, tend to lead to a mingling of the two. When the value of x becomes known with certainty, i.e., ! 2 is realized and xed, an event is red to all registered listeners to inform them that x(!) is now known:

13

public abstract class Outcome { . . . //NEW CODE BELOW: //Invoked by subclass to signal realization, x(omega) now known protected final void notifyRealization() { EventObject e = new EventObject(this); for (int i = 0; i < listeners.size(); i++) ((OutcomeListener)listeners.elementAt(i)).handleRealization(e); } }

It is the responsibility of a concrete subclass to invoke the notifyRealization method, because only the subclass can know when the outcome is realized. In practice this is generally signaled by the occurrence of some real-world event. For instance, a random travel time would be indicated by real-time telemetry when a driver arrives to his destination. The trade price of a stock would be xed when the transaction was completed. As a matter of object-oriented design, we designed the Outcome type as a class to allow us to implement its event listener functionality exactly once, in the root Outcome class. Because the class is an abstract class, we are able to specify the behavior of the methods getValue, getEstimatedValue and isRealized through abstract methods, without being bound to an implementation of those methods. In closing, note that although the event listener paradigm itself is not new, its application to handle dynamic uncertainty in the fashion we have described is new. Note also that our use of the paradigm di ers from its use in standard simulation. In standard simulation, the event listener paradigm is widely used to notify objects of the occurrence of events on the future event list. However, these events are scheduled using static uncertainty, and there is no provision made to use the event listener paradigm for updates to information other than the simple occurrence of an event. Our usage of the paradigm can handle updates to any stochastic model element, whether or not it has been scheduled as a discrete event. A major implication of the event listener paradigm, as we use it, is that online algorithms can now be implemented as listeners. Consequently, they can passively handle uncertainty by simply reacting to events as they occur. This is in stark contrast to oine algorithms, which must use complicated forecasting methods to handle uncertainty actively.

3.4. The PrimitiveOutcome Type A PrimitiveOutcome is meant to represent the outcome of a univariate random variable, for example x. In the next section we will discuss how the CompositeOutcome type can be used to handle combinations of random variables, for example z = x + y as well as multivariate random variables, for example z = (x; y). The type PrimitiveOutcome is the base class for all classes that wish to implement the Outcome of a univariate random variable:

14

public abstract class PrimitiveOutcome extends Outcome { private Object value; //x(omega), when known private boolean isRealized; //true when realized and x(omega) known, else false public PrimitiveOutcome() { super(); //Invoke superclass Outcome constructor isRealized = false; //Assume unrealized to start } public final Object getValue() throws ValueUnknownException { if (isRealized() == true) return value; else throw new ValueUnknownException(this); } public final boolean isRealized() { return isRealized; } }

The interpretation of this class is straightforward. When the random variable x generating the PrimitiveOutcome is realized (i.e., ! has been xed in ), an invocation of isRealized will return true and an invocation of getValue will return x(! ). When the random variable x generating the PrimitiveOutcome is not yet realized (i.e., ! is unknown), an invocation of isRealized will return false and an invocation of getValue will cause a ValueUnknownException to be thrown. The decisions to invoke notifyEstimate and how to implement getEstimatedValue remain deferred to a user-de ned subclass. We give an example of such a user-de ned subclass shortly. One question lingers: How does the value x(!) in the eld PrimitiveOutcome.value ever get set? To enforce measurability, that is, that x(!) be known if, and only if, the outcome is realized, we de ne a method makeRealized: public abstract class PrimitiveOutcome extends Outcome { . . . //NEW CODE BELOW: protected final void makeRealized(Object value_) { value = value_; isRealized = true; notifyRealization(); } }

This method ensures that the setting of the value and isRealized elds as well as the noti cation of listeners interested in x(!) occur together. When a subclass of PrimitiveOutcome decides that it is realized, it invokes the makeRealized method with x(!) as argument. As an example of PrimitiveOutcome, consider a univariate random variable x that is the utilization of an airplane's capacity by ight (de ned as the ratio of seats sold to the seats available). As a fraction, x is obviously bounded by the interval [0; 1]. Suppose we have some control model that uses x, and for testing purposes, we assume x to be uniformly distributed on [0; 1]. A simple implementation is given below: 15

class PrimitiveXOutcome extends PrimitiveOutcome { private String flightNumber; public Object getEstimatedValue() { return (new Double(0.5)); //the mean of a uniform [0,1] r.v. } //Invoke makeRealized when something tells me omega //for x(omega): a data feed, some other class, . . . . . . //Invoke notifyEstimate when something gives me an xhat update . . . }

Because the PrimitiveXOutcome is used in a simulation, its makeRealized method would be invoked by some other object in the simulation to indicate that the ight had departed the gate and that x was thus xed and ! determined. The value for x(!) would likely be supplied by a pseudo-random number generator (PRNG), which could later be changed to a ight information database that would supply the value x(!) upon gate closure. There is software-engineering bene t to using the Information and PrimitiveXOutcome types to represent x over a simple primitive type like double: the use of x^, for instance, is separated from its generation. When we later move the control model from testing to online implementation, we might wish to use a better value for x^, for instance a historical average of that ight. This can be done without altering client code as follows: class PrimitiveXOutcome extends PrimitiveOutcome { private String flightNumber; public Object getEstimatedValue() { return Database.getMeanUtilization(flightNumber); //historical value } //Invoke notifyEstimate when database transaction on flightNumber occurs . . . }

In general this leads to a clean separation of code modules and reduces implementation dependencies, as indicated in Figure 3. Note that the number of dependencies for m sources and n uses is reduced from the multiplicative m  n in part a to the additive m + n in part b by using the Information interface as a barrier to premature hard-coding. This can signi cantly improve coding speed. Programmers are meant to use the type PrimitiveOutcome to implement univariate random variables. Since this type represents a single outcome ! 2 , it is suitable for implementing random variables as functions of one argument, as in x : 7! F . Other than its \univariateness", there are few restrictions on PrimitiveOutcome. It can be used to represent many di erent kinds of univariate random variables by selecting the appropriate type to return from the getValue method as indicated in Table 1. The univariate random variables indicated in Table 1 are only a sample of what can be implemented with this type. All use built-in Java language types. It is the 16

PRNG

Forecasting

PRNG

Forecasting

Real−time feed

Planning

Real−time feed

Planning

Information Database

Database

Graphing

Reporting

Graphing

Reporting

a) Traditional Representations : three sources and four uses requires twelve implementations.

b) Our Representation: three sources and four uses requires seven implementations.

Figure 3: Separation of Back End from Front End (as in Appel 1998) Table 1: Univariate Random Variables and their PrimitiveOutcome.getValue Return Types getValue Return Type Type of Random Variable Integer Geometric Boolean Bernoulli Double Gaussian String Internet Encryption Color Kaleidoscope Screen Saver polymorphism of our design that allows a single type to represent many di erent random variables in the same familiar fashion. Although non-numerical types such as String and Color cannot be integrated, summed and so forth like the range space types of numerical random variables can be, they are perfectly valid software types for representing the values of random variables, particularly if they make the software easier to use or understand. As a small matter of programming, note that for some random variables, the getValue method and the getEstimatedValue method may return di erent types. For instance, consider a geometric random variable T that represents the time of the rst success in a Bernoulli process with success probability p. Although the value T (!) of T will always be an integer, implying that the return type of getValue should be Integer, an estimate T^ of T need not be an integer. For instance, the expected value E [T ] of T is a perfectly reasonable version of T^. However, E [T ] = p1 (see Cinlar 1975 for a proof), which we cannot guarantee to be an integer for arbitrary p 2 (0; 1). In this case, getEstimatedValue must return a real value, as a Double. Provided that these methods properly document their return types, this mixture of types is not a problem for client code.

3.5. The CompositeOutcome Type The CompositeOutcome type is designed to support two cases:

17

 Combinations of random variables: Combinations of random variables such as z = x1 +    + xn or the more general function of random variables z = f (x1 ; : : : ; xn ).  Multivariate random variables: Random variables in higher dimensions, such as x = (x1 ; : : : ; xn ), also known as \random vectors."

We explore each of these cases in this section. Combinations of random variables are expressed as combinations of Outcome objects in the type CompositeOutcome. For instance, suppose the airline from Section 3.4 runs n ights in a day. Then the average utilization of all ights departing on the same day is a random variable z

Pn x i z = i=1 (2) n ; where xi has replaced x as the utilization of the ith ight. While other measures of utilization may be more appropriate than z , the example that we provide in z is sucient to illustrate the main point of the exercise: how to handle CompositeOutcome objects. Similarly, we can de ne z^ as Pn x^ i z^ = i=1 (3) n ; where x^i has replaced x^ as the estimated utilization of the ith ight. This situation can be compactly handled using the COMPOSITE design pattern identi ed by Gamma et al. (1995). We de ne the type public abstract class CompositeOutcome extends Outcome implements OutcomeListener { protected Outcome[] outcomes; //Holds any number and type of Outcome objects, //including PrimitiveOutcome and CompositeOutcome objects public CompositeOutcome(Outcome[] outcomes_) { outcomes = outcomes_; . . .} }

Notice that this class can handle any integer n  1 identically|client code need not handle special cases for n = 1 versus n > 1. This is an example of number orthogonality (see Pratt and Zelkowitz 1996), whereby whatever can be done to one operand (outcomes .length == 1) can also be done to any number of operands (outcomes .length > 1). The measurability of CompositeOutcome is automatically delegated to its constituent Outcome objects by de ning the method CompositeOutcome.isRealized as the logical conjunction of the Outcome.isRealized() values from the elements of the outcomes eld: public abstract class CompositeOutcome extends Outcome implements OutcomeListener { protected Outcome[] outcomes; . . . //Composite realized only when all constituents realized public final boolean isRealized() { for (int i = 0; i < outcomes.length; i++) if (outcomes[i].isRealized() == false) return false; //Loop completed without return; hence all constituents realized return true; } }

18

Recall the random variable z from (2): z is realized if, and only if, all the xi are realized. If we could somehow model z as a CompositeOutcome, we could re-use its method isRealized \as-is" to de ne the measurability of the software object representing z in terms of the software objects representing the xi (see also Section 4.1). All that would remain for our subclass would be to de ne the methods getValue for z (!) and getEstimatedValue for z^. Fortunately, an advantage of the object-oriented programming model of Java is that a class like CompositeOutcome can be extended to form another class with more specialized behavior. We de ne just such an extended class CompositeZOutcome to represent z from (2): class CompositeZOutcome extends CompositeOutcome { CompositeZOutcome(PrimitiveXOutcome[] outcomes_) { super(outcomes_); //superclass stores array as field outcomes } public Object getValue() throws ValueUnknownException { double value = 0.0; for (int i = 0; i < outcomes.length; i++) { Outcome o = outcomes[i]; //Pass on any ValueUnknownException to the caller of this method value += ((Double)o.getValue()).doubleValue(); } return (new Double(value / outcomes.length)); } public Object getEstimatedValue() { double estimate = 0.0; for (int i = 0; i < outcomes.length; i++) { Outcome o = outcomes[i]; estimate += ((Double)o.getEstimatedValue()).doubleValue(); } return (new Double(estimate / outcomes.length)); } }

Note that there is a di erence in usage between the method CompositeOutcome.getValue and the method CompositeOutcome.getEstimatedValue. CompositeOutcome.getValue is used when all of the Outcome objects constituting the CompositeOutcome object (in its outcomes eld) are realized. If the method is invoked prematurely, a ValueUnknownException will be thrown. On the other hand, CompositeOutcome.getEstimatedValue may be invoked at any point. In summing the estimates of the individual Outcome objects constituting the CompositeZOutcome, it is immaterial whether the individual Outcome objects are actually realized or not|all we need is an estimate x^i. In fact, we could even re-write the method PrimitiveXOutcome.getEstimatedValue to return xi(!) when xi is realized and x^i as before, when xi is not realized:

19

class PrimitiveXOutcome extends PrimitiveOutcome { private String flightNumber; public Object getEstimatedValue() { //Use the realized value if available try { return (getValue()); } catch (ValueUnknownException e) { //plain estimate as before return Database.getMeanUtilization(flightNumber); //historical value } } . . . }

Because the type Object returned by the getValue and getEstimatedValue methods can be generic, the reader can easily mix random variables of di erent range spaces F from x : 7! F . For instance, the case xi : 7! Fi would be an obvious extension, with a subclass PrimitiveXiOutcome of Outcome for each xi . This is an example of combination orthogonality (Pratt and Zelkowitz 1996), whereby whatever can be done to one type (e.g., Outcome) can be done to any of its subtypes (e.g., PrimitiveX1Outcome or PrimitiveX2Outcome). Java's polymorphic type system naturally supports combination orthogonality. In general, both number and combination orthogonality are desirable design attributes of software because they lead to simpler code, with fewer exceptions and special cases to remember (Pratt and Zelkowitz 1996). Within its constructor, CompositeOutcome registers itself as an OutcomeListener on each of its constituent Outcome objects in the outcomes eld: public abstract class CompositeOutcome extends Outcome implements OutcomeListener { protected Outcome[] outcomes; public CompositeOutcome(Outcome[] outcomes_) { { outcomes = outcomes_; //Register myself as listener on my constituent outcomes for (int i = 0; i < outcomes.length; i++) outcomes[i].addOutcomeListener(this); } }

Thus when any of the xi record new estimates x^i , the combination z will notify its listeners that z^ (from (3)) has been updated:

20

public abstract class CompositeOutcome extends Outcome implements OutcomeListener { protected void handleEstimateUpdate(EventObject e) { Outcome o = (Outcome)e.getSource(); //My composite estimate is updated only if o is a constituent outcome if (myOutcome(o) == true) notifyEstimate(); } //Return true if o is one of my constituent outcomes, else false protected final boolean myOutcome(Outcome o) {. . .} }

A CompositeOutcome can legitimately listen on Outcome objects other than its constituent objects. Consequently we must be careful to verify that the source of the event being handled is indeed a constituent xi before notifying listeners of z that z^ has been updated. Similarly, realizations of constituent xi are handled by z as events: public abstract class CompositeOutcome extends Outcome implements OutcomeListener { protected void handleRealization(EventObject e) { Outcome o = (Outcome)e.getSource(); //My composite value has become realized only if this event made me so if (isRealized() == true) notifyRealization(); } }

Of course, z (!) is known only once all the individual xi are known, so individual xi realizations prior to that point are not passed on to listeners of z . Our listener-registration mechanism allows one listener to listen directly on xi and not z at the same time as another listener listens directly on z and not on xi : Information xi = . . .; Information z = . . .; //Contains xi as constituent outcome //Register to listen on xi not z OutcomeListener xilistener = . . .; xi.getOutcome(null).addOutcomeListener(xilistener); //Register to listen on z not xi OutcomeListener zlistener = . . .; z.getOutcome(null).addOutcomeListener(zlistener);

Such independent registration and listening would be useful in a program where xi and z are used for di erent purposes in unrelated contexts. Other combinations can be handled in a similar fashion, for instance,

xmax = 1max fx g in i and

xmin = 1min fx g in i

would be obvious extensions. The reader should be able to determine that these require only simple modi cations to the getEstimatedValue and getValue methods. 21

Each of these sample combinations of random variables could be modeled with equal facility as functions of random variables. ForPinstance, z can be re-written z = f (x1 ; : : : ; xn ) by de ning f x through the rule f (x1 ; : : : ; xn ) = =1 n . The Java implementation of z as the function f would be identical to its current implementation. More complex functions are also accommodated by the type CompositeOutcome, for instance f (x1 ; x2 ) = sin(x1 + x2 ), through simple modi cations to the getValue and getEstimatedValue methods. Multivariate random variables are random variables themselves composed of two or more other random variables. For instance, suppose that for i = 1; : : : ; n we have random variables xi : i 7! . The real vector x = (x1 ; : : : ; xn ) is an n-dimensional real-valued random variable. Using basic vector notation it is reasonable to write n i

i

IR

x(!) = (x1 (!1 ); : : : ; xn (!n)) where ! = (!1; : : : ; !n ) with !i 2 i; i = 1; : : : ; n

(4)

as the realized value of x. In Java, we model real numbers as the type Double. An n-dimensional real vector can be created as int n = . . .; Double[] nvector = new Double[n];

Fortunately, array types such as Double[] are subtypes of Object. This allows us to create a type CompositeMultivariateOutcome for x = (x1 ; : : : ; xn ) and use the getValue method for x(!) (from (4)) without modifying the method's signature: class CompositeMultivariateOutcome extends CompositeOutcome { CompositeMultivariateOutcome(PrimitiveXOutcome[] outcomes_) { super(outcomes_); //superclass stores array as field outcomes } public Object getValue() throws ValueUnknownException { Double[] value = new Double[outcomes.length]; for (int i = 0; i < outcomes.length; i++) { Outcome o = outcomes[i]; //Pass on any ValueUnknownException to the caller of this method value[i] = (Double)o.getValue(); } return value; } }

Similarly, it is reasonable to de ne x^ by

x^ = (^x1 ; : : : ; x^n )

(5)

as an estimate of x. Now we need only implement the method getEstimatedValue in the type CompositeMultivariateOutcome to return a vector estimate for x ^:

22

public Object getEstimatedValue() { Double[] estimate = new Double[outcomes.length]; for (int i = 0; i < outcomes.length; i++) { Outcome o = outcomes[i]; estimate[i] = (Double)o.getEstimatedValue(); } return estimate; }

The type CompositeMultivariateOutcome is a now a complete class for representing multivariate real-valued random variables. Its usage is consistent with univariate random variables: //Multivariate random variable x = (x_1, . . ., x_n) Information mrv = . . .; CompositeMultivariateOutcome o = (CompositeMultivariateOutcome)mrv.getOutome(null); //Use x(omega) or xhat, whichever is available try { Double[] xval = (Double[])o.getValue(); //Do something with xval, where xval[i] = x_i(omega) . . . } catch (ValueUnknownException e) { Double[] xest = (Double[])o.getEstimatedValue(); //Do something with xest, where xest[i] = x_ihat . . . }

Just as PrimitiveOutcome and its subtypes make it easy to model univariate random variables, similarly CompositeOutcome and its subtypes make it easy to model multivariate random variables. Note that as a matter of object-oriented programming eciency, it is unnecessary to store the result of mrv.getOutome(null) in a variable of type CompositeMultivariateOutcome. We did so merely for the sake of exposition. The preceding code example could just as well be written //Multivariate random variable x = (x_1, . . ., x_n) Information mrv = . . .; Outcome o = mrv.getOutome(null); //Eliminates wasteful cast //Same as before . . .

provided that the programmer is sure that the object returned from mrv.getOutome(null) is of type CompositeMultivariateOutcome. At this point it becomes obvious why we have used the abstract class Outcome as the superclass to both PrimitiveOutcome and CompositeOutcome. These subclasses both share the same interface, de ned by their superclass. Consequently, they can be treated virtually identically. When invoking the getValue, getEstimatedValue and addOutcomeListener methods, client code need not know whether it is handling a PrimitiveOutcome or a CompositeOutcome. Of course, client code will generally want to know the actual type returned by one of the get methods: Outcome o = . . .; Object returnValue = o.getEstimatedValue(); //Is returnValue Double, Color, . . . ???

However, the type of returnValue is not so much driven by whether o is of type PrimitiveOutcome or CompositeOutcome (witness PrimitiveXOutcome and CompositeZOutcome, both real-valued 23

with get methods returning type Double) but more by the type of the Information object generating o. Subclasses sharing the same superclass interface drives the orthogonality of Outcome, and is an example of behavioral self-similarity, whereby the parts (PrimitiveOutcome objects) behave the same as the whole (the CompositeOutcome object). The reader is referred to Heylighen (1998) and Shapiro (1999) for more details on behavioral self-similarity.

4. Issues of Probabilistic Modeling Aside from software-engineering issues like exibility and eciency, there are subtle issues of probabilistic modeling that any software representation for uncertainty must address. We have chosen to emphasize two particular issues: 1. Measurability: In layman's terms, how do we ensure that things happen in the right order? We illustrate how Java's exception handling can be used to enforce measurability and nonanticipative use of uncertain information. 2. Appropriateness: In layman's terms, how do we ensure that we don't mix apples and oranges? We illustrate how Java's re ection can be used to ensure runtime range-space compatibility across combinations of random variables. Sections 4.1 and 4.2 demonstrate how our representation resolves these important issues.

4.1. Measurability Suppose that we are given two random variables,

x : x 7!

IR

and y : y 7! : IR

It is reasonable to de ne a third random variable,

z : x  y 7!

IR

by z = x + y:

It is a simple matter to build such a combination within our representation. We could de ne x by: class XOutcome extends PrimitiveOutcome { . . . }; //Elements of Omega_x class X implements Information { . . . }; //The r.v. x

and y by: class YOutcome extends PrimitiveOutcome { . . . }; //Elements of Omega_y class Y implements Information { . . . }; //The r.v. y

This leads to the de nition of z = x + y as:

24

class ZOutcome extends CompositeOutcome { . . . //Use overloaded constructors ZOutcome(XOutcome xOutcome, YOutcome yOutcome) { super(new Outcome[] {xOutcome, yOutcome}); } public Object getValue() throws ValueUnknownException { //How do we implement this method to enforce measurability of the r.v. z wrt x and y? } }

It is obvious that we have exploited the orthogonality of the CompositeOutcome type to represent outcomes from z = x  y using outcomes from x and y . What is not so obvious, however, is how to enforce measurability of z with respect to x and y. In mathematics, and in software, we must x both !x 2 x and !y 2 y before we can x !z = (!x ; !y ) 2 z . Otherwise, we risk anticipating the information that !z encapsulates. It turns out that the object-oriented mechanism delegation can be used to resolve the issue of measurability of z . In a pleasing analogy, just as the random variable z delegates its measurability to the random variables x and y, similarly the class ZOutcome delegates its implementation of the getValue method to the method's implementation in the classes XOutcome and YOutcome: class ZOutcome extends CompositeOutcome { public Object getValue() throws ValueUnknownException { Double xValue = (Double)outcomes[0].getValue(); //xOutcome; exceptions passed to caller Double yValue = (Double)outcomes[1].getValue(); //yOutcome; exceptions passed to caller return (new Double(xValue.doubleValue() + yValue.doubleValue())); } }

In case !x is not yet xed, for instance, the line Double xValue = (Double)outcomes[0].getValue(); //xOutcome; exceptions passed to caller

will cause a ValueUnknownException to be thrown when XOutcome.getValue is invoked. Because this exception is not caught, it will be passed through to the caller of ZOutcome.getValue, who will interpret the exception as an indication that !z is not yet known. Here we see an aspect of our software design that has important mathematical bene ts: delegation automatically de nes the measurability of z (ZOutcome.getValue) in terms of that of x (XOutcome.getValue) and y (YOutcome.getValue) without any extra burden to client code. This is another example of behavioral self-similarity (Heylighen 1998, Shapiro 1999).

4.2. Appropriateness In Section 4.1, we talked of random variables x and y with the same range-space . In that case, there is no question as to the appropriateness of adding x to y to form z = x + y. Because we IR

25

can always add two real numbers, and is closed under addition, z will always have a well-de ned value for any values of x and y. However, consider the case where x : x 7! Fx and y : y 7! Fy with Fx 6= Fy . In such cases we must answer two questions: IR

1. Is it appropriate to add x(!x ) to y(!y ) for all !x 2 x and !y 2 y ? 2. If we do add x(!x ) to y(!y ), in what range-space will the result lie? Consider rst the appropriateness of adding x to y. Since our Information interface allows random variables to return values of any type (as a reference to Object) we can encounter ill-de ned operations, such as adding a string (e.g., \abc", in encryption) to a number (e.g., 106 , an optimistic bank balance). We need to prevent such ill-de nedness at runtime. De ne a mapping

M : S  S 7! ftrue; falseg;

(6)

where S is a set of range spaces of interest to the user. Elements of S should be measurable spaces, but are otherwise unrestricted. Given a pair (sx ; sy ) 2 S  S , M (sx ; sy ) indicates the appropriateness of operating with operands from the range-spaces sx and sy . Using M within our CompositeOutcome type allows for automatic checking of appropriateness. For instance, we could de ne the class class M { //Class indicates the Java type (mathematical range space) of a value static boolean isAppropriate(Class s_x, Class s_y) { . . . } }

to implement (6), so that for instance CompositeZOutcome is rede ned as

class ZOutcome extends CompositeOutcome { public Object getValue() throws ValueUnknownException { if (M.isAppropriate(outcomes[0].getClass(), outcomes[1].getClass()) != true) throw new InappropriateOperationError(outcomes[0], outcomes[1]); //Same as before . . . } }

When an ill-de ned operation is attempted, for instance if x produces strings and y produces numbers, then an unchecked exception of type InappropriateOperationError is thrown (because this is an unchecked exception, it need not be declared in a throws clause; program execution will generally stop as a result). The only remaining issue is how to implement the class M. Instead of an obvious, but tedious approach, such as a lookup-table, we prefer to use Java's re ection capabilities to implement M. Re ection in Java allows a programmer to analyze meta-level information on an object at runtime (e.g., ascertain its type and inheritance relationships). The class M can be implemented quite compactly using re ection: class M { static boolean isAppropriate(Class s_x, Class s_y) { return (s_x.isAssignableFrom(s_y) || s_y.isAssignableFrom(s_x)); } }

26

Roughly speaking, the expression s x.isAssignableFrom(s y) determines if s x is either of the same type, or a supertype of s y (see Arnold and Gosling 1996 for details). As long as s x and s y are of the same type, or one is a supertype of the other, then it is appropriate to add them. This follows quite clearly from the notion of subsets of S . For instance, the types java.lang.Integer ( ) and java.lang.Double ( ) both extend the type java.lang.Number. Accordingly, we can imagine both and as subsets of the same general superset of numbers. As long as we perform numerical arithmetic using the supertype java.lang.Number, an Information object that returns Integer values can be added to one that returns Double values because every Integer and Double can be cast to class Number when needed to generate arguments for M.isAppropriate. In fact, the notion of subsets and type-inheritance go hand-in-hand. Consider, for instance, the type hierarchy in Figure 4. In e ect, the M.isAppropriate method works by traversing the graph Z Z

IR

Z Z

IR

Information Numerical Real

Finite

String

Color

Positive

Integral

Figure 4: A Type Hierarchy De ning Appropriate Operations depicted. It determines that an operation is appropriate on two types as long as one must pass through the other on its path to the root type Information. Moreover, the types shown in italics, Finite and Positive, can be used to make secondary guarantees on the results of operations (e.g., if x and y are of type Finite or Positive, then so is z = x + y). Separating such secondary types from primary types like Real reduces the total number of types that need to be implemented. Because Java allows an interface type to extend many other interface types, we can additively combine primary and secondary types, as in: interface VPositiveInformation extends Real, Positive {};

to de ne v : v 7!

IR

+,

instead of multiplicatively de ning them:

interface RealPositive extends Real {}; //Extra type needed for single inheritance interface VPositiveInformation extends RealPositive {};

Note that the re ection-implementation of M automatically adjusts to the addition of new types, whereas a lookup-table approach requires tedious, error-prone manual additions for each new type. Unfortunately, the second question, \In what range space does the result lie?" cannot always be answered from the Information type hierarchy alone. Given two range-spaces sx and sy , the 27

result of an operation upon operands from them need not always lie in the space sx [ sy . For instance, for sx = sy = and v = x=y, y(!y ) = 0 leaves v(!v ) unde ned if x(!x ) = 0. To handle this pathological case, we would have to rely on the Java Virtual Machine to set v(!v ) to Double.NaN (which stands for \Not-a-Number"). Java does not throw exceptions during oating point arithmetic (Gosling et al. 1996), but instead uses Double.NaN as a signaling value of unde ned arithmetic results. This demonstrates a shortcoming of our re ection-based implementation of M. Because it uses only meta-level information about x and y (that they map to ), its best judgement is that v also maps to . Hence M.isAppropriate(Real.class, Real.class) would return true in this case. Only through semantic information (i.e., that y(!y ) = 0) could M deduce that v(!v ) 62 . Consequently, uniform guarantees on the range-spaces of results can be made only at compile time, by the programmer. IR

IR

IR

IR

5. Implementing the Representation The representation that we have outlined to this point has been implemented in Java 1.1.5. The extent of the implementation is given in Table 2. The code is available for experimental use Table 2: Implementation Status of the Representation Implementation Signi cance Information as interface random variable (r.v.) x Outcome as abstract class placeholder to get x(!), x^ PrimitiveOutcome as abstract class univariate r.v. CompositeOutcome as abstract class multivariate r.v. x = (x1 ; : : : ; xn ), function of r.v. f (x1 ; : : : ; xn ) ValueUnknownException as class ! not yet xed in

M as class appropriateness of operations InappropriateOperationError as class inappropriate operation Numerical as interface range space of numbers Finite not implemented Positive not implemented Type

from http://www.castlelab.princeton.edu/UncertaintyPaper/Download.html. There you can nd the implemented classes compiled in a Java ARchive (JAR) le as well as supporting documentation in JavaDoc format. Some may question the choice of Java as the language in which to implement the representation. There are many other object-oriented programming languages that could be used to implement the representation, such as C++ and SmallTalk to name just two. Our choice of implementation language was based on language support for four behaviors: 1. Polymorphism: Does the language support polymorphic types? Polymorphic types are needed to support random variables with a variety of range spaces. 2. Functional Interfaces: Does the language support the de nition of functional interfaces, 28

that is, speci cation of the behavior of a data type without speci cation of that behavior's implementation. Functional interfaces are needed to implement the functional de nition of a random variable x as in x : 7! F . 3. Event Listener Paradigm: Does the language support the event listener paradigm? In particular, does it allow for asynchronous event delivery (i.e., threads)? The event listener paradigm is needed to support noti cation to interested parties of the realization x(!) or the estimate update x^ of a random variable x. 4. Re ection: Does the language support run-time inspection and manipulation of type information (called re ection)? Re ection is needed to implement the appropriateness logic (the type M) described in Section 4.2 as well as any type hierarchies like the example presented in Figure 4. We considered three languages as candidates for implementing our representation: SmallTalk, C++ and Java. SmallTalk supports all four of the behaviors but is not widely used in the scienti c or operationsresearch communities and was thus discarded as an implementation language. C++ supports items 1{3 without issue. C++ does support limited re ection through its RunTime Type Information (RTTI) mechanism (see Pohl 1999 for a summary). Unfortunately, some implementations of C++ do not support RTTI, nor do some C++ compilers. Moreover, those C++ compilers that do support RTTI often allow programmers to disable it during the compile phase in an e ort to increase the run-time execution speed of the code. Consequently, an implementation of our representation that relied on RTTI for re ection might not compile properly in the context of a larger C++ application that relied on disabling RTTI to achieve satisfactory execution speed. Java supports all four behaviors. In particular, support for functional interfaces, the event listener paradigm and threads is built in to the language, in contrast to C++ which simulates these behaviors. Re ection in Java is supported by all implementations of Java and the virtual machine that runs it, and is part of the ocial language speci cation (Gosling et al. 1996). Moreover, Java re ection cannot be disabled, and its use inside a larger application does not slow down the rest of the application outside of the immediate execution of the re ective code sections.

6. Performance Measures and Computational Complexity Suppose some operations-research model x uses our representation for uncertainty over a discrete, nonempty time horizon T = ft1 ; : : : ; tn g. We de ne It = the number of random variables used by x at time t. Although It does not measure computational complexity, we can use it to derive N (ti; tj ) = the number of new estimates and realizations of random variables 29

in It [ It n It \ It during the interval [ti ; tj ). i

j

i

j

Larger values of N mean that x is spending more time manipulating uncertainty using any representation. We wish to estimate the computational complexity of a representation with respect to N. There are two competing approaches for handling updates/realizations in It : pull-logic and push-logic. Traditional representations generally do not use the event listener paradigm (impossible with the value representation). Instead, they poll the set It for updates/realizations at di erent times. This requires what is called pull-logic whereby it is the responsibility of those using It to `pull' changes into their own code. The computational complexity of pull-logic is analyzed in Section 6.1. In contrast, our approach uses push-logic. A user of It simply registers his code as a listener and can then \sit back and wait" because the event sources in It (Outcome objects) will `push' any updates/realizations to his code. The computational complexity of push-logic is analyzed in Section 6.2.

6.1. Pull-logic Pull-logic traditionally involves a linear scan of It . A good example of pull-logic is the future event list (FEL) in a simulation. As indicated on page 3, using a FEL to handle uncertainty works well for models with static uncertainty but does not extend elegantly to models with dynamic uncertainty. When the list is even moderately large, using a FEL to handle dynamic uncertainty can prove quite slow and from a software-engineering perspective it is a somewhat sloppy approach. Some work-arounds have been attempted, but as Banks, Carson and Nelson (1996) point out, they result in \slow runtime on computers." The computational complexity of handling updates/realizations to the elements of It will incur the per-element cost

c1 = the average number of operations required to determine if an element in It has had an update/realization relevant to x . By \relevant" we mean an update/realization that requires some action (e.g., a new computation or a re-solve). Any method that linearly scans It (e.g., with pull-logic) will incur average case complexity measured by X Cscan = c1 jIt j: t2T

6.2. Push-logic Our representation will incur an additional cost for each update/realization to uphold the list of event listeners (including initial registration) and synchronize threads in a threaded implementation. If we call this additional cost c2 , then our representation will incur an average case complexity 30

measured by

Cevent =

X tj 2T

(c1 + c2 ) N (It ; It +1 ); j

j

where N (It ; ;) = 0 (e.g., the last element of the sum). We hypothesize that for practical problems (where jIt j  N (It ; It +1 ) holds for almost all tj 2 T ) our representation will be faster than those that scan, i.e., Cevent < Cscan . Both Cscan and Cevent are posterior bounds on the complexity of handling updates/realizations. Viewed from time t0 , the size of the sets It for t 2 T , jIt j, are random quantities. Given the distribution of the random variables that compose It we could compute a priori bounds that measure the true expected complexity: E [Cscan ] and E [Cevent ]. However, since c1 and c2 are almost impossible to determine consistently, we choose to measure Cscan and Cevent experimentally. We hence forward use C^scan and C^event in place of Cscan and Cevent , respectively, to indicate that the complexity measured by our experiments (e.g., C^scan ) is only an empirical estimate of the true complexity (e.g., Cscan ). j

j

j

j

7. Computational Experiments The purpose of this section is to study the actual runtime performance of our representation. Speci cally, we want to measure how quickly models that use our representation can respond to a sequence of updates/realizations. This depends on the sensitivity of our representation to the values of jIt j and N (It ; It +1 ). It is unclear if the computational cost of Information and Outcome object allocation and maintenance will ever outweigh the bene ts of using these data types. The experiments are performed on the model given in Example 1. The model is solved using j

j

j

Example 1: Resource Allocation with Time-dependent Random Revenues

Consider the problem of assigning a limited supply of resources R (e.g., drivers) to complete a set of tasks L (e.g., pick up deliveries from customers). The revenue received for performing task l beginning at time t is r , a random variable. When a task rst becomes available, it \calls in" to make its availability known, and then waits for a resource. Some tasks become more critical over time and will call in repeatedly to o er new estimates r^ of r trying to induce the assignment of a resource to them. t

t

lt

lt

lt

two algorithms with fundamentally di erent methods of handling new updates/realizations. The rst algorithm, which we call threshold dispatch and designate xthr , is a highly locally optimal algorithm and is very quick to respond to a new update/realization. It is discussed in Section 7.1. The second algorithm, a classical auction algorithm, which we designate xauc , is a globally optimal algorithm and is slower to respond to a new update/realization. It is discussed in Section 7.2. In neither case is solution quality the issue. Instead, we are interested in how quickly a model using 31

our representation responds to a sequence of updates/realizations as compared to a model that uses a traditional representation for uncertainty. A completely thorough comparison of update/realization handling schemes would use a variety of distributions for both It (the number of new task arrivals at time tj ) and N (It ; It +1 ). As emphasized by McCormack and Sargent (1981), the shape and scale of these distributions can signi cantly a ect the distribution of jIt j depending on how long random variables persist and what their pattern of updates/realizations is. Since this is a design paper, we show only one set of results to demonstrate that our representation can be implemented to execute with reasonable eciency on realistic problems. As our experience with the representation grows, we will learn how well it works in a variety of contexts. For now, we test the representation for the parameters shown in Table 3. Tests were performed on a 300 MHz Pentium II processor with 256 MB of RAM j

j

j

j

Table 3: Parameters Used for Computational Experiments Parameter Value Rt 1000 It 1000 Task Revenues rlt Exponential (rate 1=3) Task Revenue Estimates r^lt Exponential (rate 1=3) N (It ; It +1 ) varied Task Revenue Estimate Updates Exponential (rate 1=3) j

j

running Microsoft Windows NT 4.0. The Java code was compiled under JDK 1.1.5 and run using Symantec's JIT compiler version 3.00.029. CPU times are used as a measure of C^scan and C^event . The ratio res = C^event =C^scan (7) measures the responsiveness of our representation relative to an ecient traditional representation for uncertainty that scans It (as in Section 6.1; also coded by the authors). Our interest is to establish the relationship between res and the ratio j

upd = log( jIt j = N (It ; It +1 ) ) j

j

j

(8)

that measures the intensity of updates/realizations. In e ect, upd is a measure of the relative degree of diculty that the stream of updates/realizations represents for our representation compared to traditional representations. When upd is positive we would expect our representation to do relatively better than traditional representations, because in this case the number of updates/realizations N (It ; It +1 ) is smaller than jIt j and our representation handles updates/realizations in time proportional to the former. In contrast, when upd is negative we would expect our representation to do relatively worse. j

j

j

32

7.1. Responsiveness Under xthr A simple algorithm x to generate a locally optimal assignment of resources to tasks in the example is a so-called threshold-dispatch algorithm that assigns a resource to those tasks with r^lt > rmin. When there are a large number of updates/realizations, it may be inappropriate to solve for an exact optimal solution. Instead, the threshold-dispatch algorithm presented in Figure 5 might be for

(t 2 T ) Initialization Phase: let R = R Assignment Phase: while (R 6= ; and 9 j 2 L with r^ > rmin ) assign i 2 R to task j  = arg max 2Lt fr^ g let x  = 1 let R = R n fig let L = L n fj  g a t

t

a t

t

a t

ij t a t

end

end

Comments

t

jt

j

jt

greedy, locally optimal assignment

a t

t

Figure 5: A Threshold Algorithm xthr for Online Resource Allocation an acceptable alternative where the vector xt describes the dispatches at time t with element

(

resource i is assigned to task j at time t, xijt = 10 ifotherwise. This algorithm is loosely based on the threshold model of communication networks presented by Kleinrock (1976). The set Rat is the set of resources available for dispatch at time t. Rt and Lt are required as input data for all t 2 T . It is assumed that each assignment of a resource to a task takes exactly one time period. When speed is critical, the assignment of i 2 Rat to j 2 Lt need not be the best assignment, just a reasonable one. At each time t 2 T , the dominating computational aspect for non-trivial problems will be the handling of updates/realizations in It = frjt jj 2 Lt g. As a stream of updated revenue estimates r^t is received, the algorithm can respond by reconsidering an existing task l for dispatch when r^jt > rmin. Ten runs were performed on a variety of values of N (It ; It +1 ), with rmin = 2:5 throughout. Given the parameters in Table 3, P (r > rmin) = 0:435. This is a reasonable choice for rmin because res is fairly stable in this region. Moreover, we want to avoid a situation where rmin is either too low (too many dispatches, too few updates) or too high (too few dispatches, too many updates). The results are given in Table 4. When upd is nonnegative, our representation handles updates/realizations much more eciently than representations that scan It for updates/realizations. Not surprisingly, when upd = ?1, i.e., there are on average ten updates/realizations per random variable per period, our representation is considerably slower than traditional representations. This is logical because in a traditional representation ten updates simply overwrite an existing value ten j

33

j

Table 4: Computational Results for xthr (see (7){(8) for de nitions of res and upd )

upd -1 0 1 2 3

minimum 2.190 0.245 0.133 0.129 0.128

res

mean maximum standard deviation coecient of variation 2.245 2.271 0.035900 0.01600 0.251 0.259 0.004050 0.01610 0.134 0.136 0.000853 0.00637 0.129 0.131 0.000760 0.00589 0.129 0.131 0.000878 0.00681

times. Our representation would require ten invocations of the estimateUpdated method even though only the last invocation would `count.' Evidently, our representation is not uniformly better than existing representations at handling updates/realizations. However, in our experience, upd is almost always nonnegative (for reasonable time scales), in which case our representation does prove to be the more ecient one. As a nal note, observe that the coecient of variation of res (the ratio of its standard deviation to its mean) is very low, with a standard deviation of only 0.6{1.6% of the mean. Consequently, the spread of res is omitted from further discussion. The tests of the ecient traditional representation for uncertainty that scans for updates (the denominator C^scan in the de nition of res (7)) were made as fair as possible. First, because a binary tree was used to rank tasks based on their revenue, a hash table of tree nodes was maintained. Information updating for this traditional representation required only a scan of the values in this hash table to compare the current revenue on a task with the revenue with which the task had been inserted into the tree (its tree node revenue). Only when the current revenue di ered from the task's tree node revenue was the task re-ordered in the tree. Second, the tests for the traditional representation used primitive data types like double wherever possible to avoid the overhead of object construction and referencing that is only required for our representation.

7.2. Responsiveness Under xauc The threshold dispatch algorithm xthr tested in the previous section is appropriate for applications where speed is the critical concern. However, when P (r  rmin)  0, we would expect many tasks never to be dispatched by xthr (even when there are enough resources) since their revenue will never exceed the required threshold rmin. Such online settings might be handled better by a more sophisticated algorithm. As an example, take the auction algorithm of Bertsekas (1992), which we denote xauc , that was rst introduced by the same author in Bertsekas (1979). The auction algorithm is an intuitively satisfying algorithm whereby tasks compete for resources by raising the pro ts attainable by a resource through competitive bidding. These pro ts can be thought of as dual variables. Each new bid by a task (an exogenous update to xauc ) can change such a dual variable (an endogenous update to xauc ), requiring a reaction on the part of xauc (i.e., re-optimization). In the most general case, not every resource can be assigned to every task (e.g., 34

due to union/DOT restrictions in a freight application), so we de ne the set

Rt(j ) = the set of resources in Rt that can be assigned to task j at time t 2 T to describe feasible assignments. Let i be the \pro t variable" for resource i 2 Rat . The estimated revenue for assigning resource i to task j 2 Lt will be r^jt . In the reverse auction algorithm, the assignment of resource i to task j is induced when j raises its revenue r^jt high enough to `outbid' the other tasks competing for resource i. Mathematically speaking, task j must maximize the quantity r^jt ? i , which is the incremental bid that j is willing to o er i for preference over its competing tasks. De ning Lut as the set of unassigned tasks at time t, the auction algorithm progresses as described in Figure 6. As in Section 7.1, Rt and Lt are required as input data for all for

(t 2 T ) Initialization Phase: let R = R let L = L let  = 0; 8i 2 R while (continue = true) Bidding Phase: for (j 2 L ) let i = arg max 2Rt ( ) fr^ ?  g let = max 2Rt ( ) fr^ ?  g let = max 2Rt ( ) 6= j fr^ ?  g a t u t

Comments

t

t

i

t

generally as long as L 6= ; u t

u t

j

end

i

jt

j

j

i

j

jt

j

i

j ;i

best resource for task j

i

i

jt

i

next best resource for task j ; = ?1 if R (j ) = fi g

i

j

Assignment Phase for (i 2 R ) let best(i) = fj 2 L ji = ig let j = arg max 2 best( ) f ? + g let  =  + max 2 best( ) f ? + g let J = fj : x = 1g let x = 0; 8j 2 J let L = L [ J let x i = 1 let L = L n fj g a t

u t

i

i

j

i

end

end

end

ijt u t

u t

ij t u t

u t

i

j

i

j

ijt

j

i

j

j

j

t

j

nd highest bidding task for resource i increase  by highest bidding increment i

undo previous assignments for resource i at time t update L assign resource i to task j at time t update L u t

i

u t

Figure 6: An Auction Algorithm xauc for Online Resource Allocation

t 2 T and it is assumed that each assignment of a resource to a task takes exactly one time period. Note that the constant  acts as a perturbation mechanism to prevent degeneracy. When the bidding increment j ? j is zero, more than one resource o ers maximum value to the same task j . This may create a never-ending cycle where several tasks contest a smaller number of equally

desirable resources without raising their bids. For a feasible problem, Bertsekas (1992) proves that  > 0 guarantees xauc to terminate in a nite number of iterations. Moreover, the resulting feasible solution is optimal within jLt j. 35

Each iteration of the algorithm at time t constitutes a single bidding phase followed by a single assignment phase. Most implementations of xauc continue to iterate at t until the set of unassigned tasks Lut becomes empty. In contrast to xthr , xauc guarantees a globally optimal solution at time t when  < 1=jLt j (provided costs are discretized; see Bertsekas 1992). When speed is not a dominating concern it makes sense to spend more e ort trying to nd the set of assignments that will maximize pro ts. Our tests on xauc are more extensive in scope than those performed on xthr . Speci cally, we test two types of problems: sparse and dense, which relate to the density of the underlying assignment network. In the balanced case, with n resources and n tasks, a total of n2 total assignments are possible, and the density when m  n assignments are actually feasible is

 = [m=n2 ]  100 %: Of course any feasible problem will have a density of at least 100=n%, else at least one task would have no feasible resource for assignment. In the sparse case,  = 10% while in the dense case  = 100%. As in Section 7.1, ten runs were performed for each representation on each problem. The mean results for res are presented in Table 5. The results in Table 5 paint a di erent picture than Table 5: Computational Results for xauc (see (7){(8) for de nitions of res and upd ) upd res (sparse:  = 10%) res (dense:  = 100%) -1 5.185 5.440 0 1.057 1.007 1 1.054 1.004 2 1.045 1.006 3 1.046 1.004 did those in Table 4. In this case, our representation is slower than traditional representations in handling updates/realizations. The di erence in speed is marginal at best for upd  0, never more than 6%. However, for upd = ?1 the di erence is quite pronounced. When this case dominates, an application governed by execution speed should choose a traditional scanning representation. An important observation remains: our representation is faster than traditional representations at handling updates/realizations for xthr but not at handling updates/realizations for xauc . The threshold dispatch algorithm operated by maintaining an ecient binary tree that stored tasks sorted in order of revenue. This data structure made the arg max operation in Figure 5 very ecient. In particular, it allowed xthr to react minimally to new revenue updates (re-ordering nodes is an ecient operation on binary trees). However, no such ecient update structure exists for the auction algorithm xauc . Because each resource tries to maximize its own pro ts greedily, a revenue update on task j will cause it to re-bid on all resources that can be feasibly assigned to j , in other words the entire set Rt (j ). This in turn can cause some resource i not currently assigned to j to undo its current assignment and pick j , which causes i's current task to re-bid, and so on. In the ensuing cascade of re-bids and re-assignments, the time to handle the update/realization 36

is dominated by the time required to e ect a near-complete re-solve of the problem. The fact that res improves as density increases con rms this observation (in higher density problems tasks are more tightly `coupled' so a change in revenue for one can a ect many others). In e ect, we have measured the `sensitivity' of the algorithms xthr and xauc to updates/realizations. xthr is relatively insensitive to updates/realizations because a single update/realization does not cause massive changes in primal space (decisions) while xauc is quite sensitive to updates/realizations because a single update/realization can cause massive changes in primal space.

8. Conclusion We presented a exible Java representation for uncertainty in online operations-research models. The representation has several key software-engineering advantages over traditional representations for uncertainty. Computational experiments on a realistic example demonstrated that our representation handles information updates very eciently. For some algorithms (threshold dispatch) it does so much more eciently than traditional representations, and because speed is often critical for online OR models our representation is the natural favorite on that basis alone. For other algorithms (auction) it is about as ecient as existing representations. Because the cost of using the abstract data types required by our representation is small, it is ideal for object-oriented application development because of the extreme exibility that it provides. The representation's high-level object-oriented design patterns can signi cantly increase the speed with which stochastic models can be coded. Using modern software-engineering methods has helped to create the foundation for a robust modeling toolkit for stochastic problems.

References Albers, S. 1997. Competitive online algorithms. OPTIMA: The Mathematical Programming Society Newsletter 54 1{8. Appel, A.W. 1998. Modern Compiler Implementation in Java. Cambridge University Press, New York, NY. Arnold, K., J. Gosling. 1996. The Java Programming Language. Addison-Wesley Publishing Company, Inc., New York, NY. Banks, J., J.S. Carson, II, B.L. Nelson. 1996. Discrete-Event System Simulation, 2nd ed. PrenticeHall, Upper Saddle River, NJ. Bell, D., I. Morrey, J. Pugh. 1992. Software Engineering: A Programming Approach, 2nd ed. Prentice-Hall, Englewood Cli s, NJ. Bertsekas, D.P. 1979. A distributed algorithm for the assignment problem. Working paper, M.I.T. Laboratory for Information and Decision Sciences, Cambridge, MA. 37

Bertsekas, D.P. 1992. Auction algorithms for network ow problems: A tutorial introduction. Computational Optimization and Applications 1 7{66. Bhargava, H., R. Krishnan, R. Muller. 1997a. Decision support on demand: Emerging electronic markets for decision technologies. Decision Support Systems 19 193{214. Bhargava, H., R. Krishnan, R. Muller. 1997b. Electronic commerce in decision technologies: A business cycle analysis. International Journal of Electronic Commerce 1 109{127. Birge, J.R. 1997. Stochastic programming computation and applications. INFORMS J. Computing 9 111{133. Birge, J.R., F. Louveaux. 1997. Introduction to Stochastic Programming. Springer-Verlag, New York, NY. Cinlar, E. 1975. Introduction to Stochastic Processes. Prentice-Hall, Englewood Cli s, NJ. Englander, R. 1997. Developing Java Beans. O'Reilly & Associates, Inc., Cambridge, MA. Fourer, R., D.M. Gay, B.W. Kernighan. 1993. AMPL: A Modeling Language for Mathematical Programming. The Scienti c Press, South San Francisco, CA. Gamma, E., R. Helm, R. Johnson, J. Vlissides. 1995. Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley Publishing Company, Inc., New York, NY. Gassmann, H.I., A.M. Ireland. 1995. Scenario formulation in an algebraic modelling language. Annals of Operations Research 59 45{75. Gassmann, H.I., A.M. Ireland. 1996. On the formulation of stochastic linear programs using algebraic modelling languages. Annals of Operations Research 64 83{112. Gosling, J., B. Joy, G. Steele. 1996. The Java Language Speci cation. Addison-Wesley Publishing Company, Inc., Menlo Park, CA. Heylighen, F. 1998. Principia Cybernetica Web: Web Dictionary of Cybernetics and Systems. http://pespmc1.vub.ac.be/ASC/indexASC.html. Horvitz, E.J. 1987. Reasoning about beliefs and actions under computational resource constraints. T. Levitt, ed. Proceedings of the Third Workshop on Uncertainty in Arti cial Intelligence, AAAI and Association for Uncertainty in Arti cial Intelligence, Mountain View, CA. 429{444 Horvitz, E.J. 1997. Models of continual computation. B. Weber, B. Kuipers, eds. Proceedings of the Fourteenth National Conference on Arti cial Intelligence, AAAI Press, Menlo Park, CA. Horvitz, E.J. 1998. Continual computation policies for utility-directed prefetching. G. Gardarin, J. French, N. Pissinou, K. Makki, L. Bouganim, eds. Seventh ACM Conference on Information and Knowledge Management (CIKM '98), Association for Computing Machinery Press, New York, NY. 175{184. Horvitz, E.J., G.F. Cooper, D.E. Heckerman. 1989. Re ection and action under scarce resources: Theoretical principles and empirical study. N.S. Sridharan, ed. Proceedings of the Eleventh 38

International Joint Conference on Arti cial Intelligence, Morgan Kaufmann, San Mateo, CA. 1121{1127. Ishii, N., M. Muraki. 1996. A process-variability-based online scheduling system in multi-product batch process. Computers and Chemical Engineering 20 217{234. Kall, P., J. Mayer. 1996. SLP-IOR: An interactive model management system for stochastic linear programs. Mathematical Programming 75 221{240. Karlin, A., M. Manasse, L. Rudolph, D.D. Sleator. 1988. Competitive snoopy caching. Algorithmica 3 79{119. Kernighan, B.W., D.M. Ritchie. 1988. The C Programming Language, 2nd ed. Prentice-Hall, Englewood Cli s, NJ. Keskinocak, P., R. Ravi, S. Tayur. 1998. Algorithms for reliable lead time quotation. GSIA working paper, Carnegie Mellon University, Pittsburgh, PA. Kleinrock, L. 1976. Queueing Systems, Volume 2: Computer Applications. John Wiley & Sons, Inc., New York, NY. Linsmeier, T.J., N.D. Pearson. 1996. Risk measurement: An introduction to value at risk. Technical report, University of Illinois at Urbana-Champaign, Urbana, IL. McCormack, W.M., R.G. Sargent. 1981. Analysis of future event set algorithms for discrete event simulation. Communications of the ACM 24 801{812. Paarmann, L.D., M.D. Najar. 1995. Adaptive online load forecasting via time series modeling. Electric Power Systems Research 32 219{225. Perros, H.G., K. M. Elsayed. 1996. Call admission control schemes: A review. IEEE Communications Magazine November 82{91. Pohl, I. 1999. C++ for C Programmers, 3rd ed. Addison Wesley Longman, Inc., Menlo Park, CA. Powell, W.B. 1996. A stochastic formulation of the dynamic assignment problem, with an application to truckload motor carriers. Transportation Science 30 195{219. Pratt, T.W., M.V. Zelkowitz. 1996. Programming Languages: Design and Implementation, 3rd ed. Prentice-Hall, Inc., Englewood Cli s, NJ. Shapiro, J.A. 1999. A Framework for Representing and Solving Dynamic Resource Transformation Problems. PhD thesis, Department of Operations Research and Financial Engineering, Princeton University, Princeton, NJ. Sharaf, A.M., T.L. Tjing. 1995. A novel neuro-fuzzy based self-correcting online electric load forecasting model. Electric Power Systems Research 34 121{125. Sleator, D.D., R.E. Tarjan. 1985. Amortized eciency of list update and paging rules. Communications of the ACM 28 202{208.

39

Accepted by Christopher V. Jones and John W. Chinneck; received May 1998; revised June 1999, February 2000; accepted August 2000.

40

Suggest Documents