
Top level design for Static (full prodigy language).
See bottom of this file for implementation issues.
Julie Roomy & Rob Spiger
January 6th, 1992 to Feb. 20, 1993.

Data Structures:

  Operator:  Name, Link to goal, Link to preconditions, Label, Failure 
             conditions, Effects-partially instantiated, Necessary effects.

  Literal:   Name (possible negated), Quatification and generator (may be 
             included in Name field) as variable structure, Operators, 
             Goal stack, Label, Failure condition, Parent

  Internal Node:
 	     Type (and, or, not, forall), First Operand, Rest Operand Label,
             Failure Condition, Goal Stack, Parent


----
Main:
  Over the domain for each subgoal g in d:
    a) create a PSG for g (looking for shared nodes) 

    b) label the PSG and proprogate failure/success conditions

    c) form control rules based on labeled PSG - operator and binding rules

    d) evaluate necessary effects and pre-requisits

    e) use d) to devise goal-clobbering and fatal pre-requisit violation.


----------------
Creating the PSG:
  1) Create a literal node for the goal, g.
  2) For each operator, op, that is an element of operators(g):
    a) partially instantiate O to O(goal) - binding.lisp
    b) create an operator node for O(goal) and link it to g.
    c) call Analyze_Preconditions with operator's preconditions


 ---------------------
 Analyze_Preconditions:
  1) If first term of preconditions is a predicate (ie not NOT, AND, OR
     EXISTS, FORALL) call Analyze_Literal.
  2) Otherwise:
     a) if keyword is NOT and next symbol is predicate call Analyze_Literal
        with negated predicate.
     b) otherwise:
        1) create an internal node and label it with the appropriate keyword
        2) call Analyze_Preconditions with operands.
           (ex. (and (P1) (P2) (P3)) )  Note:  If the first term is FORALL, 
           call analyze preconditions on the negation of the generator.


 ----------------
 Analyze_Literals:
  1) bind the literal using bindings generate from subgoal being bound
     to effect, and add any new bindings to the bindings for the 
     operator.
  2) if the literal matches one which already exists at this level for
     other operators, point to it, and add the bindings to match to it.
  3) Otherwise create a literal node, lit, and add it to sibling list
  4) if holds (lit goal-stack(lit)) then lable lit holds.
  5) if lit appears on goal stack label the lit gs-cycle or unknown,
     depending on whether variables match exactly.
  6) if no operator matches lit, label lit unachievable.
  7) if predicate is too expensive to expand, label it unknown.
  8) else call Creating the PSG with lit as subgoal.

  
----------------------------------------------------
----------------------------------------------------
Labeling the PSG and proprogating failure conditions:
  1) For each operator linked to the root do Label_Operator

 --------------
 Label_Operator:
  1) If node pointed to by preconditions is unlabeled
     a) if keyword then call Label_Internal_Node on preconditions.
     b) else call Label_Literal on preconditions.
  2) Operator gets same label and failure condition as the node pointed
     to by its preconditions field.

 -------------------
 Label_Internal_Node:
  1) find labels of operands:
     a) if operand is keyword then call Label_Internal_Node on it.
     b) else call Label_Literal on it.
  2) Use label charts to determine node's label and failure conditions
     based upon it's children's labels.

 -------------
 Label_Literal:
  1) For each operator which is a child of the literal, call Label_Operator
  2) if there exists an op which is lableled success label lit success
  3) else
     a) if there exists an operator which is unknown lable lit unknown
     b) else label lit failure and set lit's failure condition to conjunction
        of operators failure condition.
  note: it would be relatively easy to implement some pruning here.


-------------------------------
-------------------------------
Form Control Rules based on PSG:
  1) Traverse PSG in preorder looking for either failure or success operator
     nodes.
     a) if failure operator node:
        1) create a rule for rejecting th operator at the failure node.
           Rule's antecedent is the node's failure condition.
        2) create a rule for selecting bindings for the operator.     
           Rule's antecedent is the negation of the  node's failure condition.
        3) If the node is a pure failure node (the node does not depend
           on unknowns).  Create a rule to prefer the operator.
           Rule's antecedent is the negation of the  node's failure condition.
        4) Do not continue searching for rules below this failure node.
     b) If find a successs operator node.  Create a rule for preferring the 
        operator.


----------------------------------------
----------------------------------------
Evaluate necessary effects based on PSGs:
  1) If a root has no operators which achieve it return no necessary 
     effects.
  2) If a root does have children then:
     a)  Find the necessary effects of each operator which achieves
         the root using find-ne-of-operator.
     b)  Take the intersection of each operators effects.
     c)  Remove any necessary effects which have open or static 
         predicates.

---------------------------
Find-ne-of-operator:
  1)  Find the necessary effects of the preconditions of the
      operator using find-ne-of-internal-node
  2)  Sort the effects of the operator into effects which add things
      to the world and thigns which delete things from the world.
  3)  Remove the necessary effects from the preconditions which are
      negated by the operators effects which add to the world.
  4)  Take the union of the operators add effects and the necessary
      effects of the preconditions which were not negated.
  5)  Remove the necessary effects from (4) which are negated by the 
      operaors delete effects.
  6)  Take the union of the result (5) and the operators delete
      effects.
  7)  Remove necessary effects which have predicates which are open
      or static.

-------------------------
Find-ne-of-internal-node:

1.  Depending on the keyword of the internal node do the following:
  A.  OR
     i)  Find the necessary effects of each operand.  
    ii)  Return the intersection of the necessary effects of (i).

  B.  AND
     i)  Find the necessary effects of each operand.
    ii)  Return the union of (i) using e.

  C.  FORALL
     i)  If the generator is static or the generator is the negation
         of the f-expression.  Do the following:  (Note:  In both of
         these cases the necessary effects of the f-expression will
         arise.  In the case the generator is static subgoaling on
         the negation of the generator is useless.  In the case the
         generator is the negation of the f-expression, subgoaling on
         the negation of the generator is the same as subgoaling on 
         the f-expression.)
        a)  Store and return the necessary effects of the f-expression
            will each necessary effect given the extra condition that
            the generator be true for it.
    ii)  The generator must be non-static if (i) was not true.  Print
         a warning about the non-static generator and store and return
         nil.  (Note:  Nil is returned because one isn't sure if
         Prodigy will try to reduce the scope of the generator, or if 
         Prodigy will try to achieve the f-expression, or a
         combination thereof.  One could take the intersection of 
         necessary effects of achieving the negation of the generator
         and achieving the f-expression, but this is not possible
         because the PSG does not contain the negation of the
         generator, so one cannot find the ne of deleting the generator.)
  D.  EXISTS
     i)  Find the ne of the achieving the generator.  
    ii)  AND the extra condition to each ne from (i) that the
         generator not exist.  [Essentially, the necessary effects
         of the generator will only arise if Prodigy has to subgoal
         on creating the generator.)
   iii)  Find the necessary effects of achieving the F-expression.
    iv)  Store and return union of the effects of (ii) and (iii) by
         using effects-union.
  E. NO KEYWORD - the expression is a literal.  Use
         find-ne-of-literal.

-------------------
Find-ne-of-literal:

1.  If the node has no children, return itself as its necessary
    effect.
2.  If the node does have children then:
  A.  Find the necessary effects of each operator which achieves the
      node using find-ne-of-operator on the children.
  B.  Take the intersection of each operators effects.
  C.  Remove any ne that have open or static predicates.

Modifications:
--------------
The above computation of the necessary effects has been modified to
incorporate the bad-bindings into the necessary effects since the
necessary effects will only arise if the bad-bindings are not bound.
(See additional considerations about when picking operators (below).)

-----------------
For a more detailed description of the how the necessary effects
 are found, see "effects/effects.doc"

--------------------------------------------------------------
--------------------------------------------------------------
Devising goal preference rules based on the necessary effects:

1)  Take all possible pairs of the goals of the PSGs.
2)  If a necessary effect of goal1 negates goal2 in the
    pair then prefer goal2 over goal1.

(For more specific information, see "effects/rob-rules.doc")

--------------------------------------------------------------------
--------------------------------------------------------------------
Top level node rejection rules are created when the domain contains
unsolvable problems.  These rules take the failure conditional of 
each PSG root and create a reject rule which rejects the node containing
the PSG root literal when the failure condition evaluates to true.
--------------------------------------------------------------------
--------------------------------------------------------------------
Node and operator rejection rules are created when the necessary effects
of one goal (achieved either by a specific operator or by any operator)
will make the failure conditional of another goal evaluate to true.

--------------------------------------------------------------------
--------------------------------------------------------------------



--------------------------------------------------------------------
--------------------------------------------------------------------
NOTES AND ISSUES:
--------------------------------------------------------------------
--------------------------------------------------------------------


------------------------
--Shared-literals
------------------------
 see "shared-literals/shared-literal.doc"

------------------------
--labeling logic
------------------------
 see "label/label.doc"


------------------------
--reject operators instead of literals
------------------------
 Why not to reject a literal which has failed, but instead to 
 reject each of the operators which contribute.

          Literal (failure)
             |
             |
            OR
           |  |
          |    |
         |      |
       Op1      Op2
     

Op1 and Op2 are both labeled failure.  They have failure conditions of k1, and
k2 respectivly.
--- Approach 1:
  If a node-rejection-rule was created for the literal.  Then
the left-hand-side of the rule would be:  (AND (~ literal) k1 k2).
--- Approach 2:
  If instead two seperate rejection rules are created for the operators (or
as many rejection rules as there are operators) then the left-hand-sides 
would be  lhs1:  (k1)  lhs2: (k2).

--- Evaluating the merits of the two approaches:

At first glance it may appear that the first approach is better, since only
one rule needs to be matched, and prodigy will stop "earlier" if the rule
matches.  However, the first approach throws away information, for example
see what happens if k2 fails AND k1 does not.  In approach 1, the 
left-hand-side of the rule will not match and so the node will be rejected.
The two operators will then be tried in some undetermined order even thought 
we should know immediately that one fails.  In approach 2, prodigy will 
create the node for the literal and then reject Op1 since the left-hand-side
of its rule will match against the state.  The left-hand-side of Op2's rule
will fail to match against the state, so this operator will not be rejected.



------------------------
--immediately-applicable?
------------------------
  When generating rules for prefering success nodes, Prodigy automatically 
prefers an operator if all the preconditions for the operator hold in
the state, static checks for this using the function immediately-applicable.
If an operator is immediately-applicable static doesn't form a preference
rule thus not adding additional overhead in the form of the match cost
of the rule.

  When should immediately-applicable? return true?:

  If the failure condition of the operator is a "subset" of the precondition
  expression of the operator then the operator is immediately
  applicable.
   example:  failure condition: (and (or (P) (~ (Q) ) ) ) 
	     precondition:      (forall (G) (and (or (P) (~ (Q) ) ) ) )
  However, this is expensive to compute, so instead don't generate
  a preference rule if all of the atomic literals in the preconditions 
  of the operator are immediately-applicable.
   example:  immediately-applicable (G, P, Q)
  This is conservative in that too many preference rules will be generated.
  These preference rules will be correct but redundant.
  
  
------------------------
--mods.lisp
------------------------
  mods.lisp is loaded to redefine some prodigy and/or ebl functions.
  It should be loaded last.

------------------------
--sole-op-frules
------------------------
  When searching for failure rules - operator rejection rules - 
  the situation is examined to see if there is only one operator
  which applies.  If so the local variable sole-op is set.  After
  forming binding rules based upon the failure rules the failure
  rules in which sole-op is set are removed from *frules*.  The
  removed rules are saved in *sole-op-frules* in case someone wishes
  to use the information later.

  The removal of the rules is a decision which has advantages and
  disadvantages.
  Advantages: 
    1) Less rules mean lower overhead in terms of match cost.
    2) If there is only one operator to choose from, there is no need
         to spend time matching against the state.

  Disadvantages:
    1) Underutilizing information about when an operator will fail.


------------------------
--Conditional Effects:
------------------------
  The routine which searches for appropriate operators (relevant-operators)
  was modified to look through conditional effects lists for potential 
  operators.  The routine returns specialized information when 
  binding the goal to a conditional effect.
    When binding to a conditional effect the condition becomes "ANDed" 
  to the end of the ordinary preconditions of the operator (as stated in
  prodigy manual).  The conditional of each effect (including 
  "non-conditional" effects) is added to the effects fields of each
   operator (the condition is nil for "non-conditional" effects).
  For a better understanding of what actually happens, see the file
  "preprocess/preprocess.lisp"


------------------------
--GS-CYCLE
------------------------

    The label GS-CYCLE is not a pure failure label because we don't
want to learn op-prefer rules from goal-stack cycles.  Thus,
label-literal returns REC-FAILURE instead of (pure) FAILURE when
encountering a goal-stack cycle.  Also it is not included in the list
of pure failure labels in the function pure-failure-label?.


---------------------------------------------
----ADDITIONAL CONSIDERATIONS WHEN PICKING OPERATORS.
---------------------------------------------
There are two additional considerations about the way STATIC pick operators
to achieve literals when creating the PSG.  The first is the STATIC rules
out some operators based on typing information given in the domain specific 
information *typing-info-list*.  The second is more subtle but still has an
effect.  It is that STATIC creates the PSGs by starting with literal roots 
containing only constants, not variables.
These literal roots of the PSGs are not constants when the PSG is created 
and then are later changed to variables to create control rules.  For example,
in the blocksworld one of the PSGs is created by trying to achieve the goal
(ON G-1 G-2).  Operators are found which achieve (ON G-1 G-2).  But these 
operators will include only operators which result in things like (ON <X> <Y>) 
where the <X> and <Y> can be bound to G-1 and G-2.  If an operator results 
in the effect of adding (ON A <Y>) it will not be considered to be useful in
achieving (ON G-1 G-2) because the result doesn't match the goal.  But if goal
had been (ON <G-1> <G-2>) the operator could have been used to achieve the 
goal.  Not considering the operator which achieves (ON A <Y>) is a mistake 
when the root of the PSG is changed to (ON <G-1> <G-2>).  To fix this error
STATIC keeps track of a list of bad bindings when something like this happens.
It is stored at the literal nodes and in the case above would record that
binding <G-1> to A is not to be allowed.  Actual occurances of this arise
in the extended-strips world when goals like (INROOM G-1 G-2) are the 
root of the PSG and the operators which result in (INROOM ROBOT <X>) are 
rejected since G-1 is a constant when the PSG is created.  This information
about binding which cannot be considered are passed up both in the failure
condition for PSGs and the necessary effects.  



--------------------------------------------------------
--------------------------------------------------------
-- THINGS YET TO IMPLEMENT (not necessary in order of importance)
--------------------------------------------------------
--------------------------------------------------------

1) When finding typing information for variables in operators take the 
   typing information out of things like FORALL and EXISTS statement 
   generators.  Currently variable typing information is not stored for
   <Fx> and <Ex> variables.

2) The necessary effects of achieving (INROOM <X> <Y>) in the extended-strips
   world does not include (INROOM <X> <Y>) if <X> or <Y> are bound to a constant 
   when picking operators.  This is because one of the operators necessary effects
   will be something like (INROOM ROBOT <Y>) which does not result in 
   the more general assertion that (INROOM <X> <Y>) is true.  What possibly
   could be done is to assert that (INROOM <X> <Y>) is true if (NOT-EQUAL <X> ROBOT)
   and also that (INROOM <X> <Y>) is true if (IS-EQUAL <X> ROBOT).  This doesn't
   seem that important though.
   
3) Fix bug that shows when running extended-strips:
  My code generates the same 7 binding rules as orens.  But also generates
  binding rules for

  Carry-thru-door
  Push-box   -- for which it generates two different binding rules
      the first of which is the same as yours.
  Push-to-door -- for which it generates two different binding rules

  I looked over some of these and the differences between them suggest that
  the problem is one of more-general-rule-already-learned.  For example
  |sr9| PUTDOWN-NEXT-TO is very general.  
  Then very specific |sr10| PUTDOWN-NEXT-TO and |sr11| PUTDOWN-NEXT-TO
  are learned.  Also |sr10| and |sr11| are identical except for the numbers
  (ex: B-14 vs. B-15).

  This made me wonder about the other rules, so I checked and there are
  two prefer operator rules for PUTDOWN-NEXT-TO which are almost identical.

  There are two PUTDOWN-NEXT-TO reject operator rules the lower numbered one
  being much more general than the higher numbered one.


4) The function more-general-rule-already-learned? is a hack which
needs to be fixed.


5)   The rules generated from failure/success nodes are more specific than
   Oren's static.  The reason being that the failure condition of shared 
   literals is proprogated up the tree in this version.  This is probably
   undesirable since the rules are trying to make a choice between operators.
   In other words, if more than operator has the same expression in its 
   failure condition, the expression isn't very usefull for choosing between
   the operators.
     The only nodes which are shared are literals, so to add this 
   functionality modify the routine label-literal in "label/label-PSG.lisp".
   After determining that the literal is a failure node, check to see if it
   is shared.  If so set its failure condition to nil.
   This has been implemented in "create/create-PSG.lisp" by Rob.

6)  Remove bindings fields form InternalNodes and Literals (Literals especially
make no sense - see siblings in README).  Its usefull to leave this information
in for debugging, even though it is ambiguous.

7) Clean up compile time warnings.

8) remove duplicate loading of functions, especially those in mods.lisp which
should be loaded last.

9) Enter more domain specific information for the kite-building domain.  This would probably
   really help reduce both the size and the number of rules generated.

