Friday, December 12, 2008

Interface Design Methods for Intelligent & Complex Systems I: Context-based Programming

I’ve spent a good chunk of the last few years doing research and design around adaptive user interfaces and interfaces that include or connect to systems with some significant AI or complex system component. Over that time I’ve noticed some recurring themes and methods popping up again and again. Over the next few weeks I’m going to try to get some of them written up. Since this is my first time trying to capture the core ideas in a document feedback is very much appreciated.

Context-Based Programming
The idea of context-based programming comes from the observation that many applications, more all the time, change core behavior based on changes in their context. While it’s easy to get hung up on precisely defining ‘behavior changes’ and ‘context’, I’ve developed what I think of as a simple clarifying design method that helps me think about both issues.

Start with Data-Driven Programming

I see context-based programming as a natural extension of data-driven programming, an idea with a rich history in computer science / software engineering. Stated briefly, and from my own perspective, data-driven programming is an approach to software design that favors moving program control information from the ‘hard-coded’ language of the program to a data-structure populated from some other source (constant definitions, config file, user parameters, database, or external message). There are a lot of implications and analysis questions that go into decided how much a given program should be data-driven but they usually boil down to issues of
  • Time: how rapidly (over what time period) will a given variable need to be changed relative to other changes in the source code. For example, a user preference for large or small text in a browser should be modifiable without going back to the original programmers.
  • Difference in deployment environment: How will one deployment of the software legitimately differ from other deployments. For example, online-game players will want to choose a game server close enough on the net to insure fast response.
  • Difference in development responsibilities: Separating out development responsibilities often leads to the realization that significant differences occur in expertise, representation requirements, and time cycles. Computer games typically create level editors (data file editors) to allow artists and game designers to work a level that is comfortable to their skills and that better represents the game abstractions. The files from this editor are then input as data into the game.
There are lots of practical impacts on code reuse, software complexity, software usability, and development time that go into deciding how much of a system should be data driven, but it’s a trade off space that is (or should be) comfortable to a professional software engineer.

To sum up, data-driven programming is about paying a price for reading control data elements from a structure external to the source code in order to improve some needed system capability.

Extending Data-driven programming into Context-based programming

If a program can read in control data elements, than it can read in more than one set of control elements at different times. We can think of each set of control elements as a policy. User A (big text) or Deployment A (server 1, game level 1) has a different policy than User B (small text) or Deployment B (server 2, game level 2). If the system can load multiple policies there must be a mechanism for loading the policy. This mechanism may be as simple as a configuration file loader or data base query and happen once at program load or may involve continual update by an interactive user or by back end functions. Regardless of how it is implemented, conceptually each program that supports policies must provide some code that acts as a policy manager. The pattern then is from fully internalized source code we move some variables or constants out to external policies that are injected by a policy manager.

What about the policy manager, though? Isn’t it just another block of code that can be implemented (as needed) in a data-driven manner? What if in one circumstance, an event (system start, user action, external data update) causes the instantiation of Policy A and under another circumstance that same event should cause the instantiation of Policy B?

For example, imagine the ringer function in a cell phone software application. Typical cell phones allow the user to define a policy that includes the volume of the ringer output and the specific file to play for each member of a contact list (and incoming calls not in the contact list). This ringer policy allows each cell phone to reflect the personality of the phone owner, an important consideration in the world of consumer electronics. This policy, though is fairly rigid. My cell phone is configured to play, moderately loudly, the rowdy goofy song Yakety Sax, from the Benny Hill show when either of my two brothers call. I love this because it captures, for me, an important part of my relationship with my brothers and how I imagine them. It would, however, be embarrassing if one of them were to call while I was in a serious meeting. (Something that has happened at least once recently). To avoid this, most phones allow the default policy to be modified on the fly. At any time I can easily lower the phone volume or change the ring tone. But what if the phone designer wanted to do better? Today’s phones often have the phone owner’s calendar available to them. What if I could create my default policy (which contains Yakety Sax) and a second “meeting” policy which overrides Policy A by setting the ringer to “vibrate - audio volume = 0”. The phone would then select which policy to apply based on the externalized calendar data.

This is the essence of context-based programming. In context-based programming, the policy manager itself is designed according to data-driven programming principles. This allows the system to provide different behaviors at different times based on a set of observable conditions. This then requires a set of meta-policies that define the conditions under which the primary policies are applied. We’ve now entered the world of AI and user-adaptive systems.

A design method for Context-Based Programming

While is absolutely true that the calendar-based ringer manager described above could be designed and implemented by a skilled software engineer without ever cracking open an AI reference book (AI code is just code, after all), it is equally true that this kind of pattern-matching of observable conditions to dynamically modify system behaviors has been far more studied and described in the AI community than in the traditional software community. Particularly for meta-policies that use non-trivial conditions. What if the ringer manager needed select a ringer policy by including both the calendar, the location of the phone owner (driving or not), the users current phone use, the output modes available (volume, vibrate, screen) and the background noise level? Each of these elements, and probably a lot more, are part of the policy manager’s context and could be included in the meta-policy. The bottom line is that the larger the set of context elements available and the larger the set of possible system configurations possible, the more context-driven programming pushes for the inclusion of AI techniques including Dialog Management, Agent Based Systems, Formal Logic, Heuristic Search, Constraint-Based Reasoning, Probabilistic Reasoning, and Decision Trees.

Before I worry about whether to apply an AI technique or which AI technique to apply, I start with analyzing the system from the perspective of policies and meta-policies.
  1. What are the core changes (policy elements) that should be defined in data and how will moving these policy elements into data impact the system. What can we really do here that makes sense. (Users want to set ringer sound on their cell phone. While the cost of this may be high and involve adding memory to the phone, new management UIs, network performance issues, and server design issues, the feature will be popular enough to drive additional fee and/or ringtone sales to justify the effort)
  2. What is the life cycle of these core changes? Which of these core changes will be well supported by a set of static (not-conditional) policies. Which ones require dynamic (conditional) policies? (Users would like their ringer policies to change reflect their calendar; for other reasons phone designers are already investing in providing calendar data on phones).
  3. What are the conditions (context) that drive changing from one policy to another and what are the algorithms for organizing the context, selecting the policy, and applying the policy.
My next essay will expand on this notion by describing the adaptive-system loop and how it impacts and reflects context-driven design. Stay tuned.