/*************************************************************************** This is part of the evolver toolkit for exploring genetic progamming. Copyright (C) 1996 Benjamin Bennett and Yeasah G. Pell This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Contact information: Benjamin Bennett and Yeasah G. Pell *************************************************************************/ #ifndef __EVAL_STATE_H #define __EVAL_STATE_H /*************************************************************************** GEvalState - The state of evaluation for a given program node GHostState - The state of evaluation of the program on a host **************************************************************************/ /* prerequisites */ #include "generic.H" #include "model.H" /// The state of evaluation for a given program node /*@Doc: In order to evaluate the program the evaluator needs to know how many of the arguments to the operator have been evaluated, which operator this state applies to, and which child was evaluated last. The arguments (parameters) are necessary since the operators are postfix. The operator pointer is necessary so that the program knows what it is supposed to do, and which node in the network it should modify. And the last child evaluated is neccesary for the conditionals to decide which child to evaluate next. The reason this information needs to be explicitly stored is so that the nodes can be evaluated in ``parallel'' by executing on instruction of each, saving the state of the evaluation and moving to the next node. Because of the need to change to a different node after each instruction the evaluation can not be done recursively. */ class GEvalState : public GObject { public: /// The pointer to the operator that this state applies to GOperator *node_ptr; /// The return values of the evaluated children GArray params; /// The last evaluated child int last; GEvalState(GOperator *anode_ptr = NULL) { node_ptr = anode_ptr; } ~GEvalState() { } }; /// The state of evaluation of the program on a host class GHostState : public GObject { public: /// The stack of {\tt GEvalStates} that represents the tree evaluation GArray host_state; /// GHostState(GOperator *anode_ptr) { GEvalState *tmp = new GEvalState(anode_ptr); host_state[0] = tmp; } /// ~GHostState() { } /// Add the given operator to the states void Add(GOperator *anode_ptr) { GEvalState *tmp = new GEvalState(anode_ptr); host_state.Append(tmp); } /*@ManMemo: Evaluates one instruction and returns 1 if more work, or 0 if the top node has evaluated completely */ /*@Doc: This function provides the logic necessary to process the node at the top of the stack. If the arguments to the function are incomplete then the node pushes on the child, if the arguments are complete then the node pops the stack and placed its return value in the new top of the stack. If the stack is empty, then the program has been completely evaluated. */ int EvaluateTick(GModel *model, int node) { GEvalState *state; GOperator *head; GType *retval; int i; // get the head of the subtree to evaluate next state = host_state.Last(); head = state->node_ptr; #ifdef DEBUG_EVALUATE head->Print(); cout << endl; #endif // DEBUG_EVALUATE // check the validity of the sub-tree if(!head->Valid()) { cerr << "GHostState::EvaluateTick - Invalid tree" << endl; return -1; } // If a conditional operator if (optable.CondType(head->GetOperatorType())) { if (state->params.GetSize() == 0) { retval = NULL; i = -1; } else { retval = state->params.Pop(); i = state->last; } // ask model which child to eval next i = model->GetNext(head->GetOperatorType(), i, retval); #ifdef DEBUG_EVALUATE cout << "If evaluating child " << i << " next" << endl; #endif // DEBUG_EVALUATE if (i == -1) { // pop the stack delete host_state.Pop(); // fill in the return value if (host_state.GetSize() > 0) host_state.Last()->params.Append(retval); } else { // set last state->last = i; // push the next node onto the stack host_state.Append(new GEvalState(head->children[state->last])); } } else // not conditional { // if there are no more unevaluated parameters if (state->params.GetSize() == head->children.GetSize()) { // evaluate this node and pop the stack retval = model->Evaluate(head->GetOperatorType(), state->params, node); delete host_state.Pop(); // fill in the return value if(host_state.GetSize()) host_state.Last()->params.Append(retval); } else { // set last state->last = state->params.GetSize(); // push the next node onto the stack host_state.Append(new GEvalState(head->children[state->last])); // we have not really done any evaluation yet so try a kid if (EvaluateTick(model, node) == 0) { cout << "GHostState::EvaluateTick - Error: kid returned done" << endl; exit(1); } } } if(host_state.GetSize()) return 1; else return 0; } }; #endif //__EVAL_STATE_H