
			CONVERTING TO NNPSCM
			   Scott Huffman
			     May 1994

An implementation of NNPSCM (the "New New PSCM") will be made available as
part of Soar 6.2 (as a compile-time option), to be released soon.  The
purpose of this note is to describe how Soar6 programs can be converted to
run under NNPSCM.  We would like to see as many users as possible convert to
NNPSCM so we can get feedback about it.  We have written a conversion program
for use with SDE/Emacs that will do 95% of the work for you.

NNPSCM was described in detail by John Laird at the last Soar workshop, and
in an email note to soar-group from John and Scott dated February 17, 1994.
This note has the following parts:

1. Overview of NNPSCM and the Soar 6.2 implementation.
2. Compiling NNPSCM.
3. Converting Soar6 programs:
    (a) syntactic changes for NNPSCM
    (b) running the SDE conversion program
    (c) converting i/o links and i/o code (user-written C code)
    (d) potential conversion problems


1. Overview of NNPSCM and the Soar 6.2 implementation.
------------------------------------------------------

NNPSCM can be summarized by three basic principles (from the e-mail note of
2/17/94):

A. There is a single unique state for every goal.

B. All information about a goal and the status of problem solving in a goal
is accessible through the state (and only through the state) for the goal.

C. The only PSCM functions are those dealing with selecting and applying
operators (and state refinement).

This has the following key implications: 
I1. Problem spaces are eliminated from the context.  If an explicit problem
space symbol is to be used, it is maintained on the state.

I2. The architecture automatically generates the initial state of each goal
context.  In the Soar 6.2 implementation, this architecturally-generated
state replaces the "goal" context object generated by Soar6.  The initial
state includes a pointer to the superstate (the situation that lead to the
current goal) and information about the impasse.  Initialization of data
structures in the state (including, possibly, a problem-space symbol) is done
by operators or through refinement.

Further PSCM implications are discussied in the sources mentioned earlier.


Practically speaking, NNPSCM eliminates both "G" and "P" from Soar's goal
stack.  A trace of a NNPSCM Soar program looks like this:

  S: s1 (top-state)
  O: o1 (operator-1)
  O: o2 (operator-2)
  ==> S: s2 (operator tie) [could be one of SNC/ONC/O-tie/O-conflict/O-constraint-failure]
      O: o3 (operator-3)
    ...

At every decision cycle, either an operator is selected or a subgoal is
created (a state is generated).  For calculating I- and O-support, all
structure attached to the state symbol (e.g., s1), *except* for the ^operator
slot, is considered the "state"; the ^operator slot (and its substructure) is
considered the "operator".

State s2 looks like this:

(state s2 ^impasse tie ^choices multiple ^attribute operator
         ^item o9 o10 o11 ^superstate s1 ^quiescence t
	 ^operator o3 ^problem-space p1 ....)
  (o3 ^name operator-3 ... )
  (p1 ^name top-ps ... )

   Note that:
     * this state contains all the information that the "goal" object in
	Soar6 would contain (e.g. impasse info).
     * the "^object" pointer to a higher goal in Soar6 has been renamed 
	to "^superstate".  As in Soar6, the architecture creates this
	pointer. 
     * the problem-space structure, if it is present, is treated just as any
	other state attribute in how it is selected and for purposes of
	calculating support. 
	
	The default productions still recognize attributes for controlling
	state-copying that are placed on problem spaces (e.g.
	"one-level-attributes").  Also, "chunk-free-problem-spaces" can still
	be used.
     * since the problem-space is not a context slot, and the state is
	architecturally created instead of selected, productions cannot test
	acceptable preferences for problem-spaces or states (this is done in
	Soar6 either to create substructure of those objects before they are
	selected, or to compare potential problem-spaces/states).


2. Compiling NNPSCM
--------------------

To compile a version of Soar with NNPSCM, simply add the compiler flag
"NNPSCM" when compiling each file.  In each "make.header.<machine-type>" file
provided with the release, there are directions for where to add this flag;
simply change the line in the header file as indicated and re-compile.  In
addition, a very small change to user-written C-code (i.e. I/O code) is
required for NNPSCM, as described below.


3. Converting Soar6 programs to NNPSCM
--------------------------------------

(a.) Syntactic changes for NNPSCM.
----------------------------------
Consider a Soar6 production:

(sp test
   (goal <g> ^object <sg> ^impasse no-change ^attribute operator
	^operator <o> ^state <s>)
   (<s> ^thing <t>)
   (<t> ^isa thing)
   (<sg> ^operator <so>)
   (<o> ^name return-thing)
  -->
   (<so> ^thing <t> + &))

In NNPSCM, the production looks like this:

(sp test
   (state <g> ^superstate <sg> ^impasse no-change ^attribute operator
	^operator <o> ^thing <t>)
   (<sg> ^operator <so>)
   (<t> ^isa thing)
   (<o> ^name return-thing)
  -->
   (<so> ^thing <t> + &))


Syntactic changes from Soar6 to NNPSCM include:

(i) replacing "(goal" with "(state" in production conditions.
(ii) renaming the "^object" pointer to higher-level contexts to "^superstate"
(iii) removing any tests of acceptable preferences for problem-spaces or
states.
(iv) moving all tests of attributes of the state (e.g. "^thing <t>" in the
example above) onto the former goal object (now the architecturally-generated
state); removing the "state" slot of that object.  E.g.,

   (goal <g> ^state <s> ^impasse ....) 
   (<s> ^test t1)

 becomes
   (state <g> ^test t1 ^impasse ....)

(v) Finally, we have slightly changed the representation of input and output
links for Soar-I/O.  There is a new, architecturally-generated object "^io
<io>" on the top state; input and output links are added to this object.
This is independent of NNPSCM, but it seemed an appropriate time to make the
change.  Thus, what was:
  (<s> ^input-link i3 ^output-link o17)

becomes
  (<s> ^io i1)
  (i1 ^input-link i3 ^output-link o17)


(B) Running the SDE conversion program
---------------------------------------

We have written a conversion program that will automatically convert an
entire tree of production files for a task from Soar6 to NNPSCM.  The
conversion program requires the new release (0.6) of SDE (the Soar
Development Environment, an Emacs environment for Soar).  It handles changes
(i) - (iv) above; the I/O link change must be converted by hand (see below).

The new version of SDE tracks tasks and their files more completely.  When
you load a file of Soar productions for the first time into SDE, SDE will ask
you for the top-level load file of a task.  This should be the main file that
loads the rest of the files.  SDE will then parse that load file and
recursively scan the rest of the files to determine the tree of files that
comprises the task.  The NNPSCM conversion program uses this information to
enable conversion of whole tasks.

The conversion program is included with release 0.6 of SDE.  Two NNPSCM
conversion commands are available (M-x stands for "Meta-x" or "ESC x"):

  M-x nnpscm-convert-task

	This command will prompt you for the top-level load file of a task,
        and then convert all of the files associated with the task from Soar6
        to NNPSCM syntax.

  M-x nnpscm-convert-file

	This command will prompt you for a file name, and then convert that
	file from Soar6 to NNPSCM.

****NOTE NOTE NOTE****: BOTH CONVERSION COMMANDS ARE *DESTRUCTIVE*; THEY WILL
OVERWRITE YOUR ORIGINAL SOAR6 FILE.  Thus, before using the conversion
program, you should save a copy of your files under different names or in a
different directory.

The conversion program handles most syntax possible in Soar productions.
However, there are undoubtedly some squirrely syntaxes (e.g. multiple levels
of embedded parens and dot attribute paths) that aren't handled correctly.
In our experience these are rare and not hard to find and repair when the
productions are subsequently loaded.  

The conversion program will generate a warning when it finds a test of an
acceptable preference for a state or problem space, and remove the acceptable
preference; e.g.
   (goal <g> ^problem-space <p> + ...)
will become:
   ;; NNPSCM warning: Removed an acceptable preference test from the
   ;; following condition .....
   (state <g> ^problem-space <p> ...)

However, the conversion program will not catch every such acceptable
preference test; e.g., it won't catch something like
  (goal <g> ^object.object.state <s> +)
Again, in our experience this is fairly easy to detect.


(C) Converting I/O Links
------------------------

The conversion program does not add the extra level of indirection "^io" to
access I/O links.  This level of indirection must be added by hand.  

The new SDE provides a task-wide query-replace command (M-x sde-query-replace)
that will work over the entire set of files comprising a task, making it easy
to replace strings such as "^input-link" with "^io.input-link".

However, without any I/O link conversion, a program will run under NNPSCM if
the following two productions are added (assuming an input link named
"input-link" and an output link named "output-link"):

(sp nnpscm-conversion-hack*copy-up-input-link
   (state <s> ^io.input-link <il>)
   -->
   (<s> ^input-link <il> + &))

(sp nnpscm-conversion-hack*copy-down-output-link
   (state <s> ^output-link <ol> ^io <io>)
   -->
   (<io> ^output-link <ol> + &))

These are provided in the "convert" directory of the soar-release, in the
file "convert-prods.soar".

In addition, because the input and output links are no longer attached
directly to the top state, but rather to the top-level "io" object,
user-written C code must access the links through that io object rather than
the top state.  Soar6 exported a variable called "top_state" that users
could use to access I/O links, typically in the following ways:

(For input:)
  /* --- create the top input link --- */
  input_link_sym = get_io_sym_constant("input-link");
  top_input_link_id = get_new_io_identifier ('I');
  top_input_link_wme = add_input_wme (top_state,
                                      input_link_sym,
                                      top_input_link_id);
(For output:)
  /* --- fetch id of output link --- */
  output_link_id = get_output_value (outputs, top_state, NIL);	


NNPSCM/Soar6.2 exports a variable called "io_header" that gives access to the
io object on the top state.  This variable can be used just as "top_state"
was previously (for most users, this will simply mean query-replacing
"top_state" with "io_header").  Thus the code above will become:

(For input:)
  /* --- create the top input link --- */
  input_link_sym = get_io_sym_constant("input-link");
  top_input_link_id = get_new_io_identifier ('I');
  top_input_link_wme = add_input_wme (io_header,
                                      input_link_sym,
                                      top_input_link_id);
(For output:)
  /* --- fetch id of output link --- */
  output_link_id = get_output_value (outputs, io_header, NIL);	


(D) Potential Conversion Problems
---------------------------------

We expect that most Soar programs will convert with no problem.  However,
here are a few potential problem areas:

1. Behavior depends on selecting between problem spaces or states.

   If your program's behavior depends on being able to reason about either
alterative problem-spaces or alternative states (e.g. including tests of
acceptable preferences for those objects) it will not directly convert to
NNPSCM.  This kind of reasoning can clearly be done under NNPSCM, but it will
require a deeper conversion than the purely syntactic one described here.

2. Squirrely syntax.
 
   As mentioned above, the conversion program does not handle very squirrely
syntax, with multiple embedded dot and paren notations, etc.  It will handle
most syntactic forms used in typical soar programs.  If your programming
style involves, e.g.,  writing every production's LHS as a single condition
will all embedded syntax, you may have to do some hand conversion.  You might
consider changing your programming style while you're at it. ;)

3. Same state for multiple contexts.

   Some soar programs use the same state in multiple goal contexts; e.g.,
   
  G: g1
  S: s1
  ... 
  ==> G: g2
      S: s1
    ...

This can be useful, e.g., for operator implementation or operator subgoaling.
Under NNPSCM every context has a unique state.  Thus, to get the behavior you
want in NNPSCM, you must copy attributes of the state from higher contexts
down to lower ones.

One way to do this is to have a single object that contains all the key state
attributes; e.g.

  (state s1 ^real-state s12)
  (s12 ... state attributes...)

and copy that single pointer to lower contexts; e.g,

  (state s2 ^real-state s12)

and have all productions go through that pointer to get to the state
attributes. 

In any event, the conversion program will not do this sort of thing
automatically.

4. Previously existing attributes with "reserved" names.

NNPSCM places attributes onto the state with the names superstate, impasse,
attribute, choices, item, and quiescence; if your program uses any of these
attributes on states already there will be a conflict.  The conversion
program does not detect such cases.

Similarly, the operator slot of the state is a context slot in NNPSCM.  If
your Soar6 program used the attribute "operator" on states, under NNPSCM that
slot will be interpreted as a context slot (probably producing unexpected
behavior).

5. Same-name attributes existing on Soar6 goal and state objects in a
context.

NNPSCM merges the state and the goal of Soar6 into a single state object.
Thus, if your program creates attributes with the same name on both the state
and goal, there will be a conflict after you convert to NNPSCM.

A common example is the attribute "name" -- if your productions name both the
goal and the state, both names will be added to the state after NNPSCM
conversion, causing an attribute impasse (unless they have parallel
preferences).

6. Default productions.

  Because they are messy, we have hand-converted the default productions.
They still do most of what they do in soar6.  If your program uses the
default productions, don't convert them using the conversion program;
instead, simply load the hand-converted default productions for NNPSCM
instead of the Soar6 default productions.


Bug reports for either NNPSCM or the conversion program can be sent, as
usual, to soar-bugs@cs.cmu.edu.

