I. One more assumption As noted in your recent midterm exam, work in artificial intelligence has gone forward under a handful of assumptions: Whatever intelligence is, it results from some kind of computation and it's platform independent. It's not unique to brains. Symbol manipulation is a type of computation that is sufficient to give rise to intelligent behavior. Any symbol manipulation can be carried out on a Turing machine. Prepare to hang another foundational assumption on the wall where you've put the others. This one comes from the same folks who brought you the second assumption above...they're both part of the Physical Symbol System Hypothesis: A physical symbol system exercises its intelligence in problem solving by search -- that is, by generating and progressively modifying symbol structures until it produces a solution structure. Allen Newell and Herbert A. Simon, "Computer Science as Empirical Inquiry: Symbols and Search" The upshot of this is that lots of people in AI believe that most, if not all, problems can be mapped onto some sort of representation that looks like a graph and that those problems can be solved by some variation of a search algorithm. II. The state space The metaphor of searching a graph is also a convenient one for describing the state of a process (i.e., a program in execution). The state of a process changes over time, and at any given time the state of a process is a little slice of its history. At a very low level, the state of a process is described by the values of the arguments being passed, the instruction being executed, and if you're programming with side effects, the bindings of variables to values. (Obviously, it's easier to describe the state of a process if you don't have to worry about side effects, as there's just that much less to keep track of.) However, thinking about state at this low level becomes very tedious very quickly. So, we might be better off using a higher-level abstraction in thinking about the state of a process. Consider, for example, a program to solve the 8-tile puzzle. Instead of thinking in terms of which instruction is being executed, the values bound to arguments, and so on, we can look at the process in terms of the state of the puzzle itself. Thus, the initial state of the process would be the initial state of the puzzle. Say the initial state looks like this: 2 8 3 1 6 4 7 5 We could move any of three tiles, the 7, the 6, or the 5, to generate the three possible next states from this one: 2 8 3 1 6 4 7 5 /|\ / | \ / | \ / | \ / | \ / | \ / | \ / | \ 2 8 3 2 8 3 2 8 3 1 6 4 1 4 1 6 4 7 5 7 6 5 7 5 If we then choose, say, the lower leftmost state of those three new states, and generate the two possible next states from that one, we get this: 2 8 3 1 6 4 7 5 /|\ / | \ / | \ / | \ / | \ / | \ / | \ / | \ 2 8 3 2 8 3 2 8 3 1 6 4 1 4 1 6 4 7 5 7 6 5 7 5 / \ / \ / \ / \ / \ 2 8 3 2 8 3 6 4 1 6 4 1 7 5 7 5 Note that one of these new states is just a repeat of the initial state. We wouldn't want to explore that direction any further, because we'd just be doing work we've already done. Now, if we think of the movement of tiles as the significant operators in this process, we can describe the history of the process in terms of puzzle boards and the operators necessary to get from one board to the next. And since the nature of the operators in this case are such that only at most four new boards can be generated from any given board, we can safely say that the current behavior of the process depends on its history--the process couldn't have been in the current state without having just been in one of a very few previous states. A state-space is defined as the set of all possible states generated by the repeated application of a finite set of operators (or operations, or transformations, or moves...they're all the same thing in this context) to some initial state. In performing a state-space search, the intention is usually to find a sequence of operators that gets one from the initial state to some goal state. In the case of the 8-tile puzzle, that goal state might be: 1 2 3 8 4 7 6 5 The state space can be generated in advance, as is the case with the examples in our textbook as well as some of the examples we'll be doing in class. But it's also possible to build just the state space that's needed for exploration as the search process proceeds. Why generate the state space at run-time, and not just have it all built in advance? For some applications, that might not be much of a problem. For example, in the 8-tile puzzle, the number of different ways to arrange the tiles isn't overwhelming. At most, it might be around 9!, before subtracting impossible states. On the other hand, if you were working on a program that could play a decent game of chess, and you wanted to pre-build a data structure that was comprised of all possible boards, you'd want to make sure that you set aside a little disk space to store the approximately 10^120 (i.e., 1,000,000,000,000,000,000,000,000,000,000,000,000,000, 000,000,000,000,000,000,000,000,000,000,000,000,000,000,000, 000,000,000,000,000,000,000,000,000,000,000,000) different boards that are possible. Or maybe you'd be better off writing your program to generate just those boards that were relevant to the specific chess game it was playing at that particular time, and not worry about the rest of them. For now, though, our problems will have pre-built state spaces, and we'll worry about generating the state space as we go in later lectures. III. Examples of state-space search in the real world The state-space search is used in a lot of ways by lots of folks. For example, a compiler has a component called a parser which decomposes a high-level instruction into its component parts. But these instructions can be ambiguous, so the parser must make decisions about how various symbols (known as "tokens" in compiler world) are being used. How that decision is made depends on what the parser has already seen; in other words, the next possible state of the parsing process depends on the history of the previous states. The parser reads the input from left to right, making "guesses" as it goes. If the sequence of guesses leads to a structure for an instruction that's not legal, the parser will backtrack and systematically try new guesses, just like a depth-first search algorithm. If no combination of guesses works for the parser, you'll get a "syntax error" message. These things are sometimes called "recursive descent parsers", and you'll get to study these in your compiler course, if you're brave enough to volunteer for it. The same sorts of ideas are used to get computers to understand English and other natural languages. In fact, an entire company was founded on this idea. A guy named Gary Hendrix at the University of Texas wrote a PhD thesis on parsing English back in the late 60's or early 70's. He later took some of those same ideas and built an interface to a simple database system -- an interface that could accept data base queries in English (or at least a subset of English). He called the whole thing "Q&A", it ran on PC compatibles, and it sold off the shelf at computer stores for about $300 a copy. This product was one of the first, if not the first, offered by the company Hendrix founded, which is called "Symantec" -- a company which most of you Mac or PC owners know about, since it has swallowed up all sorts of other software vendors. Hendrix is now a zillionaire, and the moral to this story is that state-space search can make you rich. Evolutionary biologists think of all of us as the bottom layers of nodes on a very big state space. Those of us who don't have any children are the leaves on a very very big tree (well, it's not exactly a tree, but you get the idea). Some of us will generate new states (our kids) and others of us won't. Each state presumably brings humanity slightly closer to some lifeform that is perfectly adapted to the environment. (If only we could get the environment to stop changing....) Finally, as we demonstrated via our Calvin and Hobbes example, state-space search is a nice little metaphor for how we lead our lives: every decision we make is based on the chain of decisions leading up to that point. As Calvin and Hobbes illustrated however, in life, unlike in your computer, there's often no backtracking possible when you make a bad decision. IV. A first pass at the generic search algorithm So let's say you have a problem that maps onto a state space (don't we all?) and that the state space can be represented as a graph. In that graph, the individual nodes or states will represent partial solutions to the problem, the arcs between nodes will represent transformations from one partial solution to another, and the mission of the search procedure is to find a path along the arcs from some start node to some goal node. A high-level, hand-wavy description of an algorithm that carries out the search mission looks like this: Given a set of start nodes, a set of goal nodes, and a graph: make a "list" of the start nodes - let's call it the "frontier" repeat if no nodes on the frontier then terminate with failure choose one node from the frontier and remove it if the chosen node matches the goal node then terminate with success else put next nodes (the neighbors of the chosen node) on the frontier end repeat Once you have a nice search procedure like this, you can start solving simple problems like the 8-tile puzzle in an exhaustive and not-very-intelligent way. That's the example that you'll see in the powerpoint slides. After that example, we asked some important questions that we'll attempt to answer in the days to come.
Last revised: October 26, 2004