<commit_before>/* Begin CVS Header
   $Source: /Volumes/Home/Users/shoops/cvs/copasi_dev/copasi/function/CEvaluationTree.cpp,v $
   $Revision: 1.30 $
   $Name:  $
   $Author: gauges $
   $Date: 2005/07/29 15:13:28 $
   End CVS Header */

#include "copasi.h"

#include <sstream>
#include <vector>

#include "CEvaluationNode.h"
#include "CEvaluationTree.h"
#include "CFunction.h"
#include "CExpression.h"
#include "CEvaluationLexer.h"
#include "CFunctionDB.h"

#include "report/CKeyFactory.h"
#include "report/CCopasiObjectReference.h"
#include "sbml/math/ASTNode.h"
#include "utilities/CCopasiTree.h"
#include "CopasiDataModel/CCopasiDataModel.h"

const std::string CEvaluationTree::TypeName[] =
  {"userdefined", "predefined", "predefined", "userdefined", "userdefined", ""};

const char* CEvaluationTree::XMLType[] =
  {"Function", "MassAction", "PreDefined", "UserDefined", "Expression", NULL};

CEvaluationTree *
CEvaluationTree::create(CEvaluationTree::Type type)
{
  CEvaluationTree * pNew = NULL;

  switch (type)
    {
    case Function:
      pNew = new CFunction();
      break;

    case MassAction:
      pNew = new CMassAction();
      break;

    case PreDefined:
      pNew = new CKinFunction();
      pNew->setType(PreDefined);
      break;

    case UserDefined:
      pNew = new CKinFunction();
      break;

    case Expression:
      pNew = new CExpression();
      break;

    default:
      fatalError();
    }

  return pNew;
}

CEvaluationTree *
CEvaluationTree::copy(const CEvaluationTree & src)
{
  CEvaluationTree * pNew = NULL;

  switch (src.getType())
    {
    case Function:
      pNew = new CFunction(*static_cast<const CFunction *>(&src));
      break;

    case MassAction:
      pNew = new CMassAction(*static_cast<const CMassAction *>(&src));
      break;

    case PreDefined:
    case UserDefined:
      pNew = new CKinFunction(*static_cast<const CKinFunction *>(&src));
      break;

    case Expression:
      pNew = new CExpression(*static_cast<const CExpression *>(&src));
      break;

    default:
      fatalError();
    }

  return pNew;
}

CEvaluationTree::CEvaluationTree(const std::string & name,
                                 const CCopasiContainer * pParent,
                                 const CEvaluationTree::Type & type):
    CCopasiContainer(name, pParent, "Function"),
    mType(type),
    mKey(GlobalKeys.add("Function", this)),
    mInfix(),
    mErrorPosition(std::string::npos),
    mpNodeList(NULL),
    mpRoot(NULL),
    mValue(std::numeric_limits<C_FLOAT64>::quiet_NaN())
{initObjects();}

CEvaluationTree::CEvaluationTree(const CEvaluationTree & src,
                                 const CCopasiContainer * pParent):
    CCopasiContainer(src, pParent),
    mType(src.mType),
    mKey(GlobalKeys.add("Function", this)),
    mInfix(),
    mErrorPosition(std::string::npos),
    mpNodeList(NULL),
    mpRoot(NULL),
    mValue(src.mValue)
{initObjects(); setInfix(src.mInfix);}

CEvaluationTree::~CEvaluationTree()
{
  if (mpNodeList != NULL) CEvaluationParser::freeNodeList(mpNodeList);
  GlobalKeys.remove(mKey);
}

const CEvaluationTree::Type & CEvaluationTree::getType() const
{return mType;}

void CEvaluationTree::setType(const CEvaluationTree::Type & type)
{mType = type;}

const std::string & CEvaluationTree::getKey() const
  {return mKey;}

bool CEvaluationTree::setInfix(const std::string & infix)
{
  if (infix == mInfix) return true;

  mInfix = infix;

  return parse();
}

const std::string & CEvaluationTree::getInfix() const
{return mInfix;;}

std::string::size_type CEvaluationTree::getErrorPosition() const
  {return mErrorPosition;}

const std::vector< CEvaluationNode * > & CEvaluationTree::getNodeList() const
  {
    if (!mpNodeList)
      const_cast<CEvaluationTree *>(this)->mpNodeList = new std::vector< CEvaluationNode * >;
    return *mpNodeList;
  }

unsigned C_INT32 CEvaluationTree::getVariableIndex(const std::string & /*name*/) const
{return C_INVALID_INDEX;}

const C_FLOAT64 & CEvaluationTree::getVariableValue(const unsigned C_INT32 & /*index*/) const
  {
    static C_FLOAT64 Value = std::numeric_limits<C_FLOAT64>::quiet_NaN();
    return Value;
  }

C_FLOAT64 * CEvaluationTree::getObjectValue(const CCopasiObjectName & /*CN*/) const
  {return NULL;}

bool CEvaluationTree::parse()
{
  bool success = true;

  // clean up
  CEvaluationParser::freeNodeList(mpNodeList);
  mpNodeList = NULL;
  mpRoot = NULL;

  // parse the description into a linked node tree
  std::istringstream buffer(mInfix);
  CEvaluationLexer Parser(&buffer);

  CCopasiMessage::clearDeque();

  success = (Parser.yyparse() == 0);

  mpNodeList = Parser.getNodeList();
  mpRoot = Parser.getRootNode();

  // clean up if parsing failed
  if (!success)
    {
      mErrorPosition = Parser.getErrorPosition();

      CEvaluationParser::freeNodeList(mpNodeList);
      mpNodeList = NULL;
      mpRoot = NULL;
    }

  return success;
}

bool CEvaluationTree::compileNodes()
{
  if (mpNodeList == NULL) return false;

  bool success = true;

  std::vector< CEvaluationNode * >::iterator it;
  std::vector< CEvaluationNode * >::iterator end = mpNodeList->end();

  for (it = mpNodeList->begin(); it != end && success; ++it)
    success = (*it)->compile(this);

  if (!success)
    {
      end = it;
      mErrorPosition = 0;

      for (it = mpNodeList->begin(); it != end; ++it)
        mErrorPosition += (*it)->getData().length();

      mErrorPosition -= (*--it)->getData().length();
      CCopasiMessage(CCopasiMessage::ERROR, MCFunction + 3, mErrorPosition);
    }

  return success;
}

bool CEvaluationTree::setRoot(CEvaluationNode* pRootNode)
{
  if (pRootNode == NULL) return false;
  if (mpNodeList != NULL)
    CEvaluationParser::freeNodeList(mpNodeList);

  mpRoot = pRootNode;

  mpNodeList = new std::vector< CEvaluationNode * >;

  return this->updateTree();
}

bool CEvaluationTree::updateTree()
{
  if (!mpRoot || !mpNodeList) return false;
  mInfix = mpRoot->getInfix();
  mpNodeList->clear();
  CCopasiTree<CEvaluationNode>::iterator it = mpRoot;
  CCopasiTree<CEvaluationNode>::iterator end = NULL;

  for (; it != end; ++it)
    mpNodeList->push_back(&*it);

  return true;
}

bool CEvaluationTree::setTree(const ASTNode& pRootNode)
{
  return this->setRoot(CEvaluationTree::convertASTNode(pRootNode));
}

CEvaluationNode* CEvaluationTree::convertASTNode(const ASTNode& node)
{
  CEvaluationNode* pResultNode = NULL;
  ASTNodeType_t nodeType = node.getType();
  CCopasiMessage Message;
  switch (nodeType)
    {
    case AST_LAMBDA:
      // this nodetype will never be handled directly
      break;
    case AST_PLUS:
    case AST_MINUS:
    case AST_TIMES:
    case AST_DIVIDE:
    case AST_POWER:
    case AST_FUNCTION_POWER:
      // create a CEvaluationNodeOperator
      pResultNode = CEvaluationNodeOperator::createNodeFromASTTree(node);
      break;
    case AST_INTEGER:
    case AST_REAL:
    case AST_REAL_E:
    case AST_RATIONAL:
      // create a CEvaluationNodeNumber
      pResultNode = CEvaluationNodeNumber::createNodeFromASTTree(node);
      break;
    case AST_NAME:
    case AST_NAME_TIME:
      // create a CEvaluationNodeObject
      pResultNode = CEvaluationNodeObject::createNodeFromASTTree(node);
      break;
    case AST_CONSTANT_E:
    case AST_CONSTANT_PI:
    case AST_CONSTANT_FALSE:
    case AST_CONSTANT_TRUE:
      // create a CEvaluationNodeConstant
      pResultNode = CEvaluationNodeConstant::createNodeFromASTTree(node);
      break;
    case AST_FUNCTION:
      // create a function call node
      pResultNode = CEvaluationNodeCall::createNodeFromASTTree(node);
      break;
    case AST_FUNCTION_ABS:
    case AST_FUNCTION_ARCCOS:
    case AST_FUNCTION_ARCCOSH:
    case AST_FUNCTION_ARCCOT:
    case AST_FUNCTION_ARCCOTH:
    case AST_FUNCTION_ARCCSC:
    case AST_FUNCTION_ARCCSCH:
    case AST_FUNCTION_ARCSEC:
    case AST_FUNCTION_ARCSECH:
    case AST_FUNCTION_ARCSIN:
    case AST_FUNCTION_ARCSINH:
    case AST_FUNCTION_ARCTAN:
    case AST_FUNCTION_ARCTANH:
    case AST_FUNCTION_CEILING:
    case AST_FUNCTION_COS:
    case AST_FUNCTION_COSH:
    case AST_FUNCTION_COT:
    case AST_FUNCTION_COTH:
    case AST_FUNCTION_CSC:
    case AST_FUNCTION_CSCH:
    case AST_FUNCTION_EXP:
    case AST_FUNCTION_FACTORIAL:
    case AST_FUNCTION_FLOOR:
    case AST_FUNCTION_LN:
    case AST_FUNCTION_LOG:
    case AST_FUNCTION_ROOT:
    case AST_FUNCTION_SEC:
    case AST_FUNCTION_SECH:
    case AST_FUNCTION_SIN:
    case AST_FUNCTION_SINH:
    case AST_FUNCTION_TAN:
    case AST_FUNCTION_TANH:
    case AST_LOGICAL_NOT:
      // create a CEvaluationNodeFunction
      pResultNode = CEvaluationNodeFunction::createNodeFromASTTree(node);
      break;

    case AST_LOGICAL_AND:
    case AST_LOGICAL_OR:
    case AST_LOGICAL_XOR:
    case AST_RELATIONAL_EQ:
    case AST_RELATIONAL_GEQ:
    case AST_RELATIONAL_GT:
    case AST_RELATIONAL_LEQ:
    case AST_RELATIONAL_LT:
    case AST_RELATIONAL_NEQ:
      pResultNode = CEvaluationNodeLogical::createNodeFromASTTree(node);
      break;
    case AST_FUNCTION_PIECEWISE:
      pResultNode = CEvaluationNodeChoice::createNodeFromASTTree(node);
      break;
    case AST_FUNCTION_DELAY:
      // create an unsupported element error
      Message = CCopasiMessage(CCopasiMessage::EXCEPTION, MCMathML + 1,
                               node.getName());
      break;

    case AST_UNKNOWN:
      // create an unknown element error
      Message = CCopasiMessage(CCopasiMessage::EXCEPTION, MCMathML + 2);

      break;
    }
  return pResultNode;
}

const CEvaluationNode* CEvaluationTree::getRoot() const
  {
    return this->mpRoot;
  }

CEvaluationNode* CEvaluationTree::getRoot()
{
  return this->mpRoot;
}

void CEvaluationTree::initObjects()
{
  addObjectReference("Value", mValue, CCopasiObject::ValueDbl);
}

ASTNode* CEvaluationTree::toAST() const
  {
    return this->mpRoot->toAST();
  }

void CEvaluationTree::setSBMLId(const std::string& id)
{
  this->mSBMLId = id;
}

const std::string& CEvaluationTree::getSBMLId() const
  {
    return this->mSBMLId;
  }

bool CEvaluationTree::completeEvaluationTreeList(CCopasiVectorN< CEvaluationTree > & list,
    const unsigned C_INT32 & added)
{
  unsigned Added = 0;

  unsigned C_INT32 i, imax = list.size();
  CEvaluationTree * pTree;
  std::vector< CEvaluationNode * >::const_iterator it;
  std::vector< CEvaluationNode * >::const_iterator end;

  for (i = (added) ? imax - added : 0; i < imax; i++)
    {
      pTree = list[i];

      for (it = pTree->getNodeList().begin(), end = pTree->getNodeList().end(); it != end; ++it)
        {
          if (((*it)->getType() & 0xFF000000) == CEvaluationNode::CALL &&
              list.add(CCopasiDataModel::Global->getFunctionList()->loadedFunctions()[(*it)->getData()]))
            Added ++;
        }
    }

  if (Added)
    return completeEvaluationTreeList(list, Added);
  else
    return true;
}
<commit_msg>Enhanced the parse method so that MassAction are no longer creating an error message.<commit_after>/* Begin CVS Header
   $Source: /Volumes/Home/Users/shoops/cvs/copasi_dev/copasi/function/CEvaluationTree.cpp,v $
   $Revision: 1.31 $
   $Name:  $
   $Author: shoops $
   $Date: 2005/08/08 18:59:06 $
   End CVS Header */

#include "copasi.h"

#include <sstream>
#include <vector>

#include "CEvaluationNode.h"
#include "CEvaluationTree.h"
#include "CFunction.h"
#include "CExpression.h"
#include "CEvaluationLexer.h"
#include "CFunctionDB.h"

#include "report/CKeyFactory.h"
#include "report/CCopasiObjectReference.h"
#include "sbml/math/ASTNode.h"
#include "utilities/CCopasiTree.h"
#include "CopasiDataModel/CCopasiDataModel.h"

const std::string CEvaluationTree::TypeName[] =
  {"userdefined", "predefined", "predefined", "userdefined", "userdefined", ""};

const char* CEvaluationTree::XMLType[] =
  {"Function", "MassAction", "PreDefined", "UserDefined", "Expression", NULL};

CEvaluationTree *
CEvaluationTree::create(CEvaluationTree::Type type)
{
  CEvaluationTree * pNew = NULL;

  switch (type)
    {
    case Function:
      pNew = new CFunction();
      break;

    case MassAction:
      pNew = new CMassAction();
      break;

    case PreDefined:
      pNew = new CKinFunction();
      pNew->setType(PreDefined);
      break;

    case UserDefined:
      pNew = new CKinFunction();
      break;

    case Expression:
      pNew = new CExpression();
      break;

    default:
      fatalError();
    }

  return pNew;
}

CEvaluationTree *
CEvaluationTree::copy(const CEvaluationTree & src)
{
  CEvaluationTree * pNew = NULL;

  switch (src.getType())
    {
    case Function:
      pNew = new CFunction(*static_cast<const CFunction *>(&src));
      break;

    case MassAction:
      pNew = new CMassAction(*static_cast<const CMassAction *>(&src));
      break;

    case PreDefined:
    case UserDefined:
      pNew = new CKinFunction(*static_cast<const CKinFunction *>(&src));
      break;

    case Expression:
      pNew = new CExpression(*static_cast<const CExpression *>(&src));
      break;

    default:
      fatalError();
    }

  return pNew;
}

CEvaluationTree::CEvaluationTree(const std::string & name,
                                 const CCopasiContainer * pParent,
                                 const CEvaluationTree::Type & type):
    CCopasiContainer(name, pParent, "Function"),
    mType(type),
    mKey(GlobalKeys.add("Function", this)),
    mInfix(),
    mErrorPosition(std::string::npos),
    mpNodeList(NULL),
    mpRoot(NULL),
    mValue(std::numeric_limits<C_FLOAT64>::quiet_NaN())
{initObjects();}

CEvaluationTree::CEvaluationTree(const CEvaluationTree & src,
                                 const CCopasiContainer * pParent):
    CCopasiContainer(src, pParent),
    mType(src.mType),
    mKey(GlobalKeys.add("Function", this)),
    mInfix(),
    mErrorPosition(std::string::npos),
    mpNodeList(NULL),
    mpRoot(NULL),
    mValue(src.mValue)
{initObjects(); setInfix(src.mInfix);}

CEvaluationTree::~CEvaluationTree()
{
  if (mpNodeList != NULL) CEvaluationParser::freeNodeList(mpNodeList);
  GlobalKeys.remove(mKey);
}

const CEvaluationTree::Type & CEvaluationTree::getType() const
{return mType;}

void CEvaluationTree::setType(const CEvaluationTree::Type & type)
{mType = type;}

const std::string & CEvaluationTree::getKey() const
  {return mKey;}

bool CEvaluationTree::setInfix(const std::string & infix)
{
  if (infix == mInfix) return true;

  mInfix = infix;

  return parse();
}

const std::string & CEvaluationTree::getInfix() const
{return mInfix;;}

std::string::size_type CEvaluationTree::getErrorPosition() const
  {return mErrorPosition;}

const std::vector< CEvaluationNode * > & CEvaluationTree::getNodeList() const
  {
    if (!mpNodeList)
      const_cast<CEvaluationTree *>(this)->mpNodeList = new std::vector< CEvaluationNode * >;
    return *mpNodeList;
  }

unsigned C_INT32 CEvaluationTree::getVariableIndex(const std::string & /*name*/) const
{return C_INVALID_INDEX;}

const C_FLOAT64 & CEvaluationTree::getVariableValue(const unsigned C_INT32 & /*index*/) const
  {
    static C_FLOAT64 Value = std::numeric_limits<C_FLOAT64>::quiet_NaN();
    return Value;
  }

C_FLOAT64 * CEvaluationTree::getObjectValue(const CCopasiObjectName & /*CN*/) const
  {return NULL;}

bool CEvaluationTree::parse()
{
  bool success = true;

  // clean up
  CEvaluationParser::freeNodeList(mpNodeList);
  mpNodeList = NULL;
  mpRoot = NULL;

  if (mType == MassAction) return true;

  // parse the description into a linked node tree
  std::istringstream buffer(mInfix);
  CEvaluationLexer Parser(&buffer);

  CCopasiMessage::clearDeque();

  success = (Parser.yyparse() == 0);

  mpNodeList = Parser.getNodeList();
  mpRoot = Parser.getRootNode();

  // clean up if parsing failed
  if (!success)
    {
      mErrorPosition = Parser.getErrorPosition();

      CEvaluationParser::freeNodeList(mpNodeList);
      mpNodeList = NULL;
      mpRoot = NULL;
    }

  return success;
}

bool CEvaluationTree::compileNodes()
{
  if (mpNodeList == NULL) return false;

  bool success = true;

  std::vector< CEvaluationNode * >::iterator it;
  std::vector< CEvaluationNode * >::iterator end = mpNodeList->end();

  for (it = mpNodeList->begin(); it != end && success; ++it)
    success = (*it)->compile(this);

  if (!success)
    {
      end = it;
      mErrorPosition = 0;

      for (it = mpNodeList->begin(); it != end; ++it)
        mErrorPosition += (*it)->getData().length();

      mErrorPosition -= (*--it)->getData().length();
      CCopasiMessage(CCopasiMessage::ERROR, MCFunction + 3, mErrorPosition);
    }

  return success;
}

bool CEvaluationTree::setRoot(CEvaluationNode* pRootNode)
{
  if (pRootNode == NULL) return false;
  if (mpNodeList != NULL)
    CEvaluationParser::freeNodeList(mpNodeList);

  mpRoot = pRootNode;

  mpNodeList = new std::vector< CEvaluationNode * >;

  return this->updateTree();
}

bool CEvaluationTree::updateTree()
{
  if (!mpRoot || !mpNodeList) return false;
  mInfix = mpRoot->getInfix();
  mpNodeList->clear();
  CCopasiTree<CEvaluationNode>::iterator it = mpRoot;
  CCopasiTree<CEvaluationNode>::iterator end = NULL;

  for (; it != end; ++it)
    mpNodeList->push_back(&*it);

  return true;
}

bool CEvaluationTree::setTree(const ASTNode& pRootNode)
{
  return this->setRoot(CEvaluationTree::convertASTNode(pRootNode));
}

CEvaluationNode* CEvaluationTree::convertASTNode(const ASTNode& node)
{
  CEvaluationNode* pResultNode = NULL;
  ASTNodeType_t nodeType = node.getType();
  CCopasiMessage Message;
  switch (nodeType)
    {
    case AST_LAMBDA:
      // this nodetype will never be handled directly
      break;
    case AST_PLUS:
    case AST_MINUS:
    case AST_TIMES:
    case AST_DIVIDE:
    case AST_POWER:
    case AST_FUNCTION_POWER:
      // create a CEvaluationNodeOperator
      pResultNode = CEvaluationNodeOperator::createNodeFromASTTree(node);
      break;
    case AST_INTEGER:
    case AST_REAL:
    case AST_REAL_E:
    case AST_RATIONAL:
      // create a CEvaluationNodeNumber
      pResultNode = CEvaluationNodeNumber::createNodeFromASTTree(node);
      break;
    case AST_NAME:
    case AST_NAME_TIME:
      // create a CEvaluationNodeObject
      pResultNode = CEvaluationNodeObject::createNodeFromASTTree(node);
      break;
    case AST_CONSTANT_E:
    case AST_CONSTANT_PI:
    case AST_CONSTANT_FALSE:
    case AST_CONSTANT_TRUE:
      // create a CEvaluationNodeConstant
      pResultNode = CEvaluationNodeConstant::createNodeFromASTTree(node);
      break;
    case AST_FUNCTION:
      // create a function call node
      pResultNode = CEvaluationNodeCall::createNodeFromASTTree(node);
      break;
    case AST_FUNCTION_ABS:
    case AST_FUNCTION_ARCCOS:
    case AST_FUNCTION_ARCCOSH:
    case AST_FUNCTION_ARCCOT:
    case AST_FUNCTION_ARCCOTH:
    case AST_FUNCTION_ARCCSC:
    case AST_FUNCTION_ARCCSCH:
    case AST_FUNCTION_ARCSEC:
    case AST_FUNCTION_ARCSECH:
    case AST_FUNCTION_ARCSIN:
    case AST_FUNCTION_ARCSINH:
    case AST_FUNCTION_ARCTAN:
    case AST_FUNCTION_ARCTANH:
    case AST_FUNCTION_CEILING:
    case AST_FUNCTION_COS:
    case AST_FUNCTION_COSH:
    case AST_FUNCTION_COT:
    case AST_FUNCTION_COTH:
    case AST_FUNCTION_CSC:
    case AST_FUNCTION_CSCH:
    case AST_FUNCTION_EXP:
    case AST_FUNCTION_FACTORIAL:
    case AST_FUNCTION_FLOOR:
    case AST_FUNCTION_LN:
    case AST_FUNCTION_LOG:
    case AST_FUNCTION_ROOT:
    case AST_FUNCTION_SEC:
    case AST_FUNCTION_SECH:
    case AST_FUNCTION_SIN:
    case AST_FUNCTION_SINH:
    case AST_FUNCTION_TAN:
    case AST_FUNCTION_TANH:
    case AST_LOGICAL_NOT:
      // create a CEvaluationNodeFunction
      pResultNode = CEvaluationNodeFunction::createNodeFromASTTree(node);
      break;

    case AST_LOGICAL_AND:
    case AST_LOGICAL_OR:
    case AST_LOGICAL_XOR:
    case AST_RELATIONAL_EQ:
    case AST_RELATIONAL_GEQ:
    case AST_RELATIONAL_GT:
    case AST_RELATIONAL_LEQ:
    case AST_RELATIONAL_LT:
    case AST_RELATIONAL_NEQ:
      pResultNode = CEvaluationNodeLogical::createNodeFromASTTree(node);
      break;
    case AST_FUNCTION_PIECEWISE:
      pResultNode = CEvaluationNodeChoice::createNodeFromASTTree(node);
      break;
    case AST_FUNCTION_DELAY:
      // create an unsupported element error
      Message = CCopasiMessage(CCopasiMessage::EXCEPTION, MCMathML + 1,
                               node.getName());
      break;

    case AST_UNKNOWN:
      // create an unknown element error
      Message = CCopasiMessage(CCopasiMessage::EXCEPTION, MCMathML + 2);

      break;
    }
  return pResultNode;
}

const CEvaluationNode* CEvaluationTree::getRoot() const
  {
    return this->mpRoot;
  }

CEvaluationNode* CEvaluationTree::getRoot()
{
  return this->mpRoot;
}

void CEvaluationTree::initObjects()
{
  addObjectReference("Value", mValue, CCopasiObject::ValueDbl);
}

ASTNode* CEvaluationTree::toAST() const
  {
    return this->mpRoot->toAST();
  }

void CEvaluationTree::setSBMLId(const std::string& id)
{
  this->mSBMLId = id;
}

const std::string& CEvaluationTree::getSBMLId() const
  {
    return this->mSBMLId;
  }

bool CEvaluationTree::completeEvaluationTreeList(CCopasiVectorN< CEvaluationTree > & list,
    const unsigned C_INT32 & added)
{
  unsigned Added = 0;

  unsigned C_INT32 i, imax = list.size();
  CEvaluationTree * pTree;
  std::vector< CEvaluationNode * >::const_iterator it;
  std::vector< CEvaluationNode * >::const_iterator end;

  for (i = (added) ? imax - added : 0; i < imax; i++)
    {
      pTree = list[i];

      for (it = pTree->getNodeList().begin(), end = pTree->getNodeList().end(); it != end; ++it)
        {
          if (((*it)->getType() & 0xFF000000) == CEvaluationNode::CALL &&
              list.add(CCopasiDataModel::Global->getFunctionList()->loadedFunctions()[(*it)->getData()]))
            Added ++;
        }
    }

  if (Added)
    return completeEvaluationTreeList(list, Added);
  else
    return true;
}
<|endoftext|>


<commit_before>#include "dsa_common.h"

#include "list_merger.h"

#include "core/client.h"
#include "link.h"
#include "message/response/list_response_message.h"
#include "module/logger.h"
#include "stream/requester/incoming_list_stream.h"

namespace dsa {
IncomingListCache::IncomingListCache(){};
IncomingListCache::IncomingListCache(ref_<ListMerger>&& merger,
                                     IncomingListCache::Callback&& callback,
                                     bool list_profile)
    : _merger(std::move(merger)),
      _callback(std::move(callback)),
      _list_profile(list_profile) {}

void IncomingListCache::destroy_impl() {
  _merger->remove(get_ref());
  _merger.reset();
  if (!_callback_running) {
    _callback = nullptr;
  }
}

void IncomingListCache::_receive_update(const std::vector<string_>& update) {
  if (_callback != nullptr) {
    BEFORE_CALLBACK_RUN();
    _callback(*this, update);
    AFTER_CALLBACK_RUN();
  }
}

const VarMap& IncomingListCache::get_map() const { return _merger->_map; }
const VarMap& IncomingListCache::get_profile_map() const {
  return *_merger->_profile_map;
}
Status IncomingListCache::get_status() const { return _merger->_last_status; }
const string_& IncomingListCache::get_last_pub_path() const {
  return _merger->_last_pub_path;
}

ListMerger::ListMerger(ref_<DsLink>&& link, const string_& path)
    : _link(std::move(link)), _path(path) {}
ListMerger::~ListMerger() {}

void ListMerger::destroy_impl() {
  _link->_list_mergers.erase(_path);
  _link.reset();

  if (_stream != nullptr) {
    _stream->close();
    // stream can't be destroyed here to make sure the request is canceled
    _stream.reset();
  }
  if (_profile_list_cache != nullptr) {
    _profile_list_cache->close();
    _profile_list_cache.reset();
  }

  for (auto it : _caches) {
    it->close();
  }
  _caches.clear();

  _changes.clear();
}

ref_<IncomingListCache> ListMerger::list(IncomingListCache::Callback&& callback,
                                         bool list_profile) {
  if (list_profile) {
    _profile_required = true;
  }
  IncomingListCache* cache =
      new IncomingListCache(get_ref(), std::move(callback), list_profile);
  _caches.emplace(cache);

  if (_stream == nullptr) {
    _stream = _link->_client->get_session().requester.list(
        _path,
        CAST_LAMBDA(IncomingListStreamCallback)[this, copy_ref = get_ref()](
            IncomingListStream & stream,
            ref_<const ListResponseMessage> && msg) {
          if (msg) {
            new_list_response(std::move(msg));
          }
        });
  } else if (_last_status != Status::INITIALIZING) {
    // send a fresh update
    if (!list_profile || _profile_list_cache == nullptr ||
        _profile_map != nullptr) {
      // if profile is needed, check if
      cache->_receive_update({});
    }
  }
  return cache->get_ref();
}
void ListMerger::list_profile() {
  if (_profile_list_cache != nullptr) {
    _profile_list_cache->close();
  }
  // if $is and pub_path both exists
  auto is_str = _map.at("$is").to_string();
  _profile_list_cache = _link->list(
      _last_pub_path + "/" + is_str,
      [ this, keepref = get_ref() ](IncomingListCache & cache,
                                    const std::vector<string_>& str) mutable {
        _profile_map = make_ref_<VarMap>(cache.get_map());
        if (_last_status != Status::INITIALIZING) {
          // profile only udpate
          update_caches({}, true);
        }
      },
      false);
}
void ListMerger::update_caches(std::vector<string_> changes,
                               bool profile_only) {
  _iterating_caches = true;
  bool removed_some = false;
  for (auto it = _caches.begin(); it != _caches.end();) {
    if ((*it)->_list_profile) {
      if (_profile_list_cache != nullptr && _profile_map == nullptr) {
        // profile is reuqired, and it's availible, but not loaded yet
        ++it;
        continue;
      }
    } else if (profile_only) {
      // profile only updates is not need for list cache that doesn't require it
      ++it;
      continue;
    }
    (*it)->_receive_update(changes);
    if ((*it)->is_destroyed()) {
      // remove() is blocked, need to handle it here
      it = _caches.erase(it);
      removed_some = true;
    } else {
      ++it;
    }
  }
  if (removed_some) {
    if (_caches.empty()) {
      destroy();
    }
  }
  _iterating_caches = false;
  _changes.clear();
}
void ListMerger::new_list_response(ref_<const ListResponseMessage>&& message) {
  bool refreshed = message->get_refreshed();
  _last_status = message->get_status();
  _last_pub_path = message->get_pub_path();

  if (refreshed) {
    _map.clear();
    _changes.clear();
  }
  auto& map = message->get_map();

  for (auto& it : map) {
    if (it.second->is_blank()) {
      _map.erase(it.first);
    } else {
      _map[it.first] = it.second->get_value();
    }
    if (!refreshed) {
      if (std::find(_changes.begin(), _changes.end(), it.first) ==
          _changes.end()) {
        _changes.push_back(it.first);
      }
    }
  }
  if (_profile_required && _profile_list_cache == nullptr &&
      _map.count("$is") > 0 && !_last_pub_path.empty()) {
    list_profile();
  }
  if (_last_status != Status::INITIALIZING) {
    // normal update
    update_caches(_changes);
  }
}

void ListMerger::remove(const ref_<IncomingListCache>& cache) {
  if (is_destroyed() || _iterating_caches) return;

  _caches.erase(cache);
  if (_caches.empty()) {
    destroy();
  }
}
}  // namespace dsa
<commit_msg>allow get_profile_map when it's not loaded<commit_after>#include "dsa_common.h"

#include "list_merger.h"

#include "core/client.h"
#include "link.h"
#include "message/response/list_response_message.h"
#include "module/logger.h"
#include "stream/requester/incoming_list_stream.h"

namespace dsa {
IncomingListCache::IncomingListCache(){};
IncomingListCache::IncomingListCache(ref_<ListMerger>&& merger,
                                     IncomingListCache::Callback&& callback,
                                     bool list_profile)
    : _merger(std::move(merger)),
      _callback(std::move(callback)),
      _list_profile(list_profile) {}

void IncomingListCache::destroy_impl() {
  _merger->remove(get_ref());
  _merger.reset();
  if (!_callback_running) {
    _callback = nullptr;
  }
}

void IncomingListCache::_receive_update(const std::vector<string_>& update) {
  if (_callback != nullptr) {
    BEFORE_CALLBACK_RUN();
    _callback(*this, update);
    AFTER_CALLBACK_RUN();
  }
}

const VarMap& IncomingListCache::get_map() const { return _merger->_map; }
const VarMap& IncomingListCache::get_profile_map() const {
  static const VarMap emptymap;
  if (_merger->_profile_map != nullptr) {
    return *_merger->_profile_map;
  } else {
    return emptymap;
  }
}
Status IncomingListCache::get_status() const { return _merger->_last_status; }
const string_& IncomingListCache::get_last_pub_path() const {
  return _merger->_last_pub_path;
}

ListMerger::ListMerger(ref_<DsLink>&& link, const string_& path)
    : _link(std::move(link)), _path(path) {}
ListMerger::~ListMerger() {}

void ListMerger::destroy_impl() {
  _link->_list_mergers.erase(_path);
  _link.reset();

  if (_stream != nullptr) {
    _stream->close();
    // stream can't be destroyed here to make sure the request is canceled
    _stream.reset();
  }
  if (_profile_list_cache != nullptr) {
    _profile_list_cache->close();
    _profile_list_cache.reset();
  }

  for (auto it : _caches) {
    it->close();
  }
  _caches.clear();

  _changes.clear();
}

ref_<IncomingListCache> ListMerger::list(IncomingListCache::Callback&& callback,
                                         bool list_profile) {
  if (list_profile) {
    _profile_required = true;
  }
  IncomingListCache* cache =
      new IncomingListCache(get_ref(), std::move(callback), list_profile);
  _caches.emplace(cache);

  if (_stream == nullptr) {
    _stream = _link->_client->get_session().requester.list(
        _path,
        CAST_LAMBDA(IncomingListStreamCallback)[this, copy_ref = get_ref()](
            IncomingListStream & stream,
            ref_<const ListResponseMessage> && msg) {
          if (msg) {
            new_list_response(std::move(msg));
          }
        });
  } else if (_last_status != Status::INITIALIZING) {
    // send a fresh update
    if (!list_profile || _profile_list_cache == nullptr ||
        _profile_map != nullptr) {
      // if profile is needed, check if
      cache->_receive_update({});
    }
  }
  return cache->get_ref();
}
void ListMerger::list_profile() {
  if (_profile_list_cache != nullptr) {
    _profile_list_cache->close();
  }
  // if $is and pub_path both exists
  auto is_str = _map.at("$is").to_string();
  _profile_list_cache = _link->list(
      _last_pub_path + "/" + is_str,
      [ this, keepref = get_ref() ](IncomingListCache & cache,
                                    const std::vector<string_>& str) mutable {
        _profile_map = make_ref_<VarMap>(cache.get_map());
        if (_last_status != Status::INITIALIZING) {
          // profile only udpate
          update_caches({}, true);
        }
      },
      false);
}
void ListMerger::update_caches(std::vector<string_> changes,
                               bool profile_only) {
  _iterating_caches = true;
  bool removed_some = false;
  for (auto it = _caches.begin(); it != _caches.end();) {
    if ((*it)->_list_profile) {
      if (_profile_list_cache != nullptr && _profile_map == nullptr) {
        // profile is reuqired, and it's availible, but not loaded yet
        ++it;
        continue;
      }
    } else if (profile_only) {
      // profile only updates is not need for list cache that doesn't require it
      ++it;
      continue;
    }
    (*it)->_receive_update(changes);
    if ((*it)->is_destroyed()) {
      // remove() is blocked, need to handle it here
      it = _caches.erase(it);
      removed_some = true;
    } else {
      ++it;
    }
  }
  if (removed_some) {
    if (_caches.empty()) {
      destroy();
    }
  }
  _iterating_caches = false;
  _changes.clear();
}
void ListMerger::new_list_response(ref_<const ListResponseMessage>&& message) {
  bool refreshed = message->get_refreshed();
  _last_status = message->get_status();
  _last_pub_path = message->get_pub_path();

  if (refreshed) {
    _map.clear();
    _changes.clear();
  }
  auto& map = message->get_map();

  for (auto& it : map) {
    if (it.second->is_blank()) {
      _map.erase(it.first);
    } else {
      _map[it.first] = it.second->get_value();
    }
    if (!refreshed) {
      if (std::find(_changes.begin(), _changes.end(), it.first) ==
          _changes.end()) {
        _changes.push_back(it.first);
      }
    }
  }
  if (_profile_required && _profile_list_cache == nullptr &&
      _map.count("$is") > 0 && !_last_pub_path.empty()) {
    list_profile();
  }
  if (_last_status != Status::INITIALIZING) {
    // normal update
    update_caches(_changes);
  }
}

void ListMerger::remove(const ref_<IncomingListCache>& cache) {
  if (is_destroyed() || _iterating_caches) return;

  _caches.erase(cache);
  if (_caches.empty()) {
    destroy();
  }
}
}  // namespace dsa
<|endoftext|>


<commit_before>// @(#)root/cont:$Id$
// Author: Philippe Canal October 2013

/*************************************************************************
 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TViewPubFunctions                                                    //
//                                                                      //
// View implementing the TList interface and giving access all the      //
// TFunction describing public methods in a class and all its base      //
// classes without caching any of the TFunction pointers.               //
//                                                                      //
// Adding to this collection directly is prohibited.                    //
// Iteration can only be done via the TIterator interfaces.             //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "TViewPubFunctions.h"

#include "TClass.h"
#include "TBaseClass.h"
#include "TError.h"
#include "TFunction.h"
#include "THashList.h"

// ClassImp(TViewPubFunctions)

////////////////////////////////////////////////////////////////////////////////
/// loop over all base classes and add them to the container.

static void AddBasesClasses(TList &bases, TClass *cl)
{
   TIter nextBaseClass(cl->GetListOfBases());
   TBaseClass *base;
   while ((base = (TBaseClass*) nextBaseClass())) {
      if (!base->GetClassPointer()) continue;
      if (!(base->Property() & kIsPublic)) continue;

      bases.Add(base->GetClassPointer());
      AddBasesClasses(bases,base->GetClassPointer());
   }
}

////////////////////////////////////////////////////////////////////////////////
/// Usual constructor

TViewPubFunctions::TViewPubFunctions(TClass *cl /* = 0 */)
{
   if (cl) {
      fClasses.Add(cl);
      AddBasesClasses(fClasses,cl);
   }
}

////////////////////////////////////////////////////////////////////////////////
/// Default destructor.

TViewPubFunctions::~TViewPubFunctions()
{
}

////////////////////////////////////////////////////////////////////////////////
/// Clear is not allowed in this class.
/// See TList::Clear for the intended behavior.

void TViewPubFunctions::Clear(Option_t * /* option="" */)
{
   ::Error("TViewPubFunctions::Clear","Operation not allowed on a view.");
}

////////////////////////////////////////////////////////////////////////////////
/// Delete is not allowed in this class.
/// See TList::Delete for the intended behavior.

void TViewPubFunctions::Delete(Option_t * /*option="" */)
{
   ::Error("TViewPubFunctions::Delete","Operation not allowed on a view.");
}

////////////////////////////////////////////////////////////////////////////////
/// Find an object in this list using its name. Requires a sequential
/// scan till the object has been found. Returns 0 if object with specified
/// name is not found.

TObject *TViewPubFunctions::FindObject(const char * name) const
{
   if (name==0 || name[0]==0) return 0;

   TIter next(&fClasses);
   while (TClass *cl = (TClass*)next()) {
      THashList *hl = dynamic_cast<THashList*>(cl->GetListOfMethods(kFALSE));
      TIter funcnext(hl->GetListForObject(name));
      while (TFunction *p = (TFunction*) funcnext())
         if (p->Property() & kIsPublic
             && strncmp(p->GetName(),name,strlen(p->GetName())) == 0)
            return p;
   }
   return 0;
}

////////////////////////////////////////////////////////////////////////////////
/// Find an object in this list using the object's IsEqual()
/// member function. Requires a sequential scan till the object has
/// been found. Returns 0 if object is not found.

TObject *TViewPubFunctions::FindObject(const TObject * obj) const
{
   TIter next(&fClasses);
   while (TClass *cl = (TClass*)next()) {
      TObject *result = cl->GetListOfMethods(kFALSE)->FindObject(obj);
      if (result) return result;
   }
   return 0;
}

////////////////////////////////////////////////////////////////////////////////
/// Return a list iterator.

TIterator *TViewPubFunctions::MakeIterator(Bool_t dir /* = kIterForward*/) const
{
   return new TViewPubFunctionsIter(this, dir);
}

////////////////////////////////////////////////////////////////////////////////
/// AddFirst is not allowed in this class.
/// See TList::AddFirst for the intended behavior.

void TViewPubFunctions::AddFirst(TObject * /* obj */)
{
   ::Error("TViewPubFunctions::AddFirst","Operation not allowed on a view.");
}

////////////////////////////////////////////////////////////////////////////////
/// AddFirst is not allowed in this class.
/// See TList::AddFirst for the intended behavior.

void TViewPubFunctions::AddFirst(TObject * /* obj */, Option_t * /* opt */)
{
   ::Error("TViewPubFunctions::AddFirst","Operation not allowed on a view.");
}

////////////////////////////////////////////////////////////////////////////////
/// AddLast is not allowed in this class.
/// See TList::AddLast for the intended behavior.

void TViewPubFunctions::AddLast(TObject * /* obj */)
{
   ::Error("TViewPubFunctions::AddLast","Operation not allowed on a view.");
}

////////////////////////////////////////////////////////////////////////////////
/// AddLast is not allowed in this class.
/// See TList::AddLast for the intended behavior.

void TViewPubFunctions::AddLast(TObject * /* obj */, Option_t * /* opt */)
{
   ::Error("TViewPubFunctions::AddLast","Operation not allowed on a view.");
}

////////////////////////////////////////////////////////////////////////////////
/// AddAt is not allowed in this class.
/// See TList::AddAt for the intended behavior.

void TViewPubFunctions::AddAt(TObject * /* obj */, Int_t /* idx */)
{
   ::Error("TViewPubFunctions::AddAt","Operation not allowed on a view.");
}

////////////////////////////////////////////////////////////////////////////////
/// AddAfter is not allowed in this class.
/// See TList::AddAfter for the intended behavior.

void TViewPubFunctions::AddAfter(const TObject * /* after */, TObject * /* obj */)
{
   ::Error("TViewPubFunctions::RemAddLastove","Operation not allowed on a view.");
}

////////////////////////////////////////////////////////////////////////////////
/// AddAfter is not allowed in this class.
/// See TList::AddAfter for the intended behavior.

void TViewPubFunctions::AddAfter(TObjLink * /* after */, TObject * /* obj */)
{
   ::Error("TViewPubFunctions::AddAfter","Operation not allowed on a view.");
}

////////////////////////////////////////////////////////////////////////////////
/// AddBefore is not allowed in this class.
/// See TList::AddBefore for the intended behavior.

void TViewPubFunctions::AddBefore(const TObject * /* before */, TObject * /* obj */)
{
   ::Error("TViewPubFunctions::AddBefore","Operation not allowed on a view.");
}

////////////////////////////////////////////////////////////////////////////////
/// AddBefore is not allowed in this class.
/// See TList::AddBefore for the intended behavior.

void TViewPubFunctions::AddBefore(TObjLink * /* before */, TObject * /* obj */)
{
   ::Error("TViewPubFunctions::AddBefore","Operation not allowed on a view.");
}

////////////////////////////////////////////////////////////////////////////////
/// Returns the object at position idx. Returns 0 if idx is out of range.

TObject  *TViewPubFunctions::At(Int_t idx) const
{
   Int_t i = 0;
   TIter next(&fClasses);
   while (TClass *cl = (TClass*)next()) {
      TIter funcnext(cl->GetListOfMethods(kFALSE));
      while (TFunction *p = (TFunction*) funcnext()) {
         if (p->Property() & kIsPublic) {
            if (i == idx) return p;
            ++i;
         }
      }
   }
   return 0;
}

////////////////////////////////////////////////////////////////////////////////
/// After is not allowed in this class.
/// See TList::After for the intended behavior.

TObject  *TViewPubFunctions::After(const TObject * /* obj */) const
{
   ::Error("TViewPubFunctions::After","Operation not allowed on a view.");
   return 0;
}

////////////////////////////////////////////////////////////////////////////////
/// Before is not allowed in this class.
/// See TList::Before for the intended behavior.

TObject  *TViewPubFunctions::Before(const TObject * /* obj */) const
{
   ::Error("TViewPubFunctions::Before","Operation not allowed on a view.");
   return 0;
}

////////////////////////////////////////////////////////////////////////////////
/// First is not allowed in this class.
/// See TList::First for the intended behavior.

TObject  *TViewPubFunctions::First() const
{
   ::Error("TViewPubFunctions::First","Operation not allowed on a view.");
   return 0;
}

////////////////////////////////////////////////////////////////////////////////
/// FirstLink is not allowed in this class.
/// See TList::FirstLink for the intended behavior.

TObjLink *TViewPubFunctions::FirstLink() const
{
   ::Error("TViewPubFunctions::FirstLink","Operation not allowed on a view.");
   return 0;
}

////////////////////////////////////////////////////////////////////////////////
/// GetObjectRef is not allowed in this class.
/// See TList::GetObjectRef for the intended behavior.

TObject **TViewPubFunctions::GetObjectRef(const TObject * /* obj */) const
{
   ::Error("TViewPubFunctions::GetObjectRef","Operation not yet allowed on a view.");
   return 0;
}

////////////////////////////////////////////////////////////////////////////////
/// Return the total number of public methods (currently loaded in the list
/// of functions) in this class and all its base classes.

Int_t TViewPubFunctions::GetSize() const
{
   Int_t size = 0;
   TIter next(&fClasses);
   while (TClass *cl = (TClass*)next()) {
      TIter funcnext(cl->GetListOfMethods(kFALSE));
      while (TFunction *p = (TFunction*) funcnext())
         if (p->Property() & kIsPublic) ++size;
   }
   return size;

}

////////////////////////////////////////////////////////////////////////////////
/// Load all the functions known to the intepreter for the scope 'fClass'
/// and all its bases classes.

void TViewPubFunctions::Load()
{
   TIter next(&fClasses);
   while (TClass *cl = (TClass*)next()) {
      cl->GetListOfMethods(kTRUE);
   }
}

////////////////////////////////////////////////////////////////////////////////
/// Last is not allowed in this class.
/// See TList::Last for the intended behavior.

TObject  *TViewPubFunctions::Last() const
{
   ::Error("TViewPubFunctions::Last","Operation not allowed on a view.");
   return 0;
}

////////////////////////////////////////////////////////////////////////////////
/// LastLink is not allowed in this class.
/// See TList::LastLink for the intended behavior.

TObjLink *TViewPubFunctions::LastLink() const
{
   ::Error("TViewPubFunctions::LastLink","Operation not allowed on a view.");
   return 0;
}

////////////////////////////////////////////////////////////////////////////////
/// RecursiveRemove is not allowed in this class.
/// See TList::RecursiveRemove for the intended behavior.

void TViewPubFunctions::RecursiveRemove(TObject * /* obj */)
{
   ::Error("TViewPubFunctions::RecursiveRemove","Operation not allowed on a view.");
}

////////////////////////////////////////////////////////////////////////////////
/// Remove is not allowed in this class.
/// See TList::Remove for the intended behavior.

TObject   *TViewPubFunctions::Remove(TObject * /* obj */)
{
   ::Error("TViewPubFunctions::Remove","Operation not allowed on a view.");
   return 0;
}

////////////////////////////////////////////////////////////////////////////////
/// Remove is not allowed in this class.
/// See TList::Remove for the intended behavior.

TObject   *TViewPubFunctions::Remove(TObjLink * /* lnk */)
{
   ::Error("TViewPubFunctions::Remove","Operation not allowed on a view.");
   return 0;
}

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TViewPubFunctionsIter                                                //
//                                                                      //
// Iterator of over the view's content                                  //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

// ClassImp(TViewPubFunctionsIter)

////////////////////////////////////////////////////////////////////////////////
/// Create a new list iterator. By default the iteration direction
/// is kIterForward. To go backward use kIterBackward.

TViewPubFunctionsIter::TViewPubFunctionsIter(const TViewPubFunctions *l, Bool_t dir)
: fView(l),fClassIter(l->GetListOfClasses(),dir), fFuncIter((TCollection *)0),
  fStarted(kFALSE), fDirection(dir)
{
}

////////////////////////////////////////////////////////////////////////////////
/// Copy ctor.

TViewPubFunctionsIter::TViewPubFunctionsIter(const TViewPubFunctionsIter &iter) :
   TIterator(iter), fView(iter.fView),
   fClassIter(iter.fClassIter), fFuncIter(iter.fFuncIter),
   fStarted(iter.fStarted), fDirection(iter.fDirection)
{
}

////////////////////////////////////////////////////////////////////////////////
/// Overridden assignment operator.

TIterator &TViewPubFunctionsIter::operator=(const TIterator &rhs)
{
   const TViewPubFunctionsIter *iter = dynamic_cast<const TViewPubFunctionsIter*>(&rhs);
   if (this != &rhs && iter) {
      fView      = iter->fView;
      fClassIter = iter->fClassIter;
      fFuncIter  = iter->fFuncIter;
      fStarted   = iter->fStarted;
      fDirection = iter->fDirection;
   }
   return *this;
}

////////////////////////////////////////////////////////////////////////////////
/// Overloaded assignment operator.

TViewPubFunctionsIter &TViewPubFunctionsIter::operator=(const TViewPubFunctionsIter &rhs)
{
   if (this != &rhs) {
      fView      = rhs.fView;
      fClassIter = rhs.fClassIter;
      fFuncIter  = rhs.fFuncIter;
      fStarted   = rhs.fStarted;
      fDirection = rhs.fDirection;
   }
   return *this;
}

////////////////////////////////////////////////////////////////////////////////
/// Return next object in the list. Returns 0 when no more objects in list.

TObject *TViewPubFunctionsIter::Next()
{
   if (!fView) return 0;

   if (!fStarted) {
      TClass *current = (TClass*)fClassIter();
      fStarted = kTRUE;
      if (current) {
         fFuncIter.~TIter();
         new (&(fFuncIter)) TIter(current->GetListOfMethods(kFALSE),fDirection);
      } else {
         return 0;
      }
   }

   while (1) {

      TFunction *func = (TFunction *)fFuncIter();
      if (!func) {
         // End of list of functions, move to the next;
         TClass *current = (TClass*)fClassIter();
         if (current) {
            fFuncIter.~TIter();
            new (&(fFuncIter)) TIter(current->GetListOfMethods(kFALSE),fDirection);
            continue;
         } else {
            return 0;
         }
      } else if (func->Property() & kIsPublic) {
         // If it is public we found the next one.
         return func;
      }

   }
   // Not reacheable.
   return 0;
}

////////////////////////////////////////////////////////////////////////////////
/// Reset list iterator.

void TViewPubFunctionsIter::Reset()
{
   fStarted = kFALSE;
   fClassIter.Reset();
}

////////////////////////////////////////////////////////////////////////////////
/// This operator compares two TIterator objects.

Bool_t TViewPubFunctionsIter::operator!=(const TIterator &aIter) const
{
   const TViewPubFunctionsIter *iter = dynamic_cast<const TViewPubFunctionsIter*>(&aIter);
   if (iter) {
      return (fClassIter != iter->fClassIter || fFuncIter != iter->fFuncIter);
   }
   return false; // for base class we don't implement a comparison
}

////////////////////////////////////////////////////////////////////////////////
/// This operator compares two TViewPubFunctionsIter objects.

Bool_t TViewPubFunctionsIter::operator!=(const TViewPubFunctionsIter &aIter) const
{
   return (fClassIter != aIter.fClassIter || fFuncIter != aIter.fFuncIter);
}

<commit_msg>Doxygen and spelling<commit_after>// @(#)root/cont:$Id$
// Author: Philippe Canal October 2013

/*************************************************************************
 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

/** \class TViewPubFunctions
View implementing the TList interface and giving access all the
TFunction describing public methods in a class and all its base
classes without caching any of the TFunction pointers.

Adding to this collection directly is prohibited.
Iteration can only be done via the TIterator interfaces.
*/

#include "TViewPubFunctions.h"

#include "TClass.h"
#include "TBaseClass.h"
#include "TError.h"
#include "TFunction.h"
#include "THashList.h"

// ClassImp(TViewPubFunctions)

////////////////////////////////////////////////////////////////////////////////
/// loop over all base classes and add them to the container.

static void AddBasesClasses(TList &bases, TClass *cl)
{
   TIter nextBaseClass(cl->GetListOfBases());
   TBaseClass *base;
   while ((base = (TBaseClass*) nextBaseClass())) {
      if (!base->GetClassPointer()) continue;
      if (!(base->Property() & kIsPublic)) continue;

      bases.Add(base->GetClassPointer());
      AddBasesClasses(bases,base->GetClassPointer());
   }
}

////////////////////////////////////////////////////////////////////////////////
/// Usual constructor

TViewPubFunctions::TViewPubFunctions(TClass *cl /* = 0 */)
{
   if (cl) {
      fClasses.Add(cl);
      AddBasesClasses(fClasses,cl);
   }
}

////////////////////////////////////////////////////////////////////////////////
/// Default destructor.

TViewPubFunctions::~TViewPubFunctions()
{
}

////////////////////////////////////////////////////////////////////////////////
/// Clear is not allowed in this class.
/// See TList::Clear for the intended behavior.

void TViewPubFunctions::Clear(Option_t * /* option="" */)
{
   ::Error("TViewPubFunctions::Clear","Operation not allowed on a view.");
}

////////////////////////////////////////////////////////////////////////////////
/// Delete is not allowed in this class.
/// See TList::Delete for the intended behavior.

void TViewPubFunctions::Delete(Option_t * /*option="" */)
{
   ::Error("TViewPubFunctions::Delete","Operation not allowed on a view.");
}

////////////////////////////////////////////////////////////////////////////////
/// Find an object in this list using its name. Requires a sequential
/// scan till the object has been found. Returns 0 if object with specified
/// name is not found.

TObject *TViewPubFunctions::FindObject(const char * name) const
{
   if (name==0 || name[0]==0) return 0;

   TIter next(&fClasses);
   while (TClass *cl = (TClass*)next()) {
      THashList *hl = dynamic_cast<THashList*>(cl->GetListOfMethods(kFALSE));
      TIter funcnext(hl->GetListForObject(name));
      while (TFunction *p = (TFunction*) funcnext())
         if (p->Property() & kIsPublic
             && strncmp(p->GetName(),name,strlen(p->GetName())) == 0)
            return p;
   }
   return 0;
}

////////////////////////////////////////////////////////////////////////////////
/// Find an object in this list using the object's IsEqual()
/// member function. Requires a sequential scan till the object has
/// been found. Returns 0 if object is not found.

TObject *TViewPubFunctions::FindObject(const TObject * obj) const
{
   TIter next(&fClasses);
   while (TClass *cl = (TClass*)next()) {
      TObject *result = cl->GetListOfMethods(kFALSE)->FindObject(obj);
      if (result) return result;
   }
   return 0;
}

////////////////////////////////////////////////////////////////////////////////
/// Return a list iterator.

TIterator *TViewPubFunctions::MakeIterator(Bool_t dir /* = kIterForward*/) const
{
   return new TViewPubFunctionsIter(this, dir);
}

////////////////////////////////////////////////////////////////////////////////
/// AddFirst is not allowed in this class.
/// See TList::AddFirst for the intended behavior.

void TViewPubFunctions::AddFirst(TObject * /* obj */)
{
   ::Error("TViewPubFunctions::AddFirst","Operation not allowed on a view.");
}

////////////////////////////////////////////////////////////////////////////////
/// AddFirst is not allowed in this class.
/// See TList::AddFirst for the intended behavior.

void TViewPubFunctions::AddFirst(TObject * /* obj */, Option_t * /* opt */)
{
   ::Error("TViewPubFunctions::AddFirst","Operation not allowed on a view.");
}

////////////////////////////////////////////////////////////////////////////////
/// AddLast is not allowed in this class.
/// See TList::AddLast for the intended behavior.

void TViewPubFunctions::AddLast(TObject * /* obj */)
{
   ::Error("TViewPubFunctions::AddLast","Operation not allowed on a view.");
}

////////////////////////////////////////////////////////////////////////////////
/// AddLast is not allowed in this class.
/// See TList::AddLast for the intended behavior.

void TViewPubFunctions::AddLast(TObject * /* obj */, Option_t * /* opt */)
{
   ::Error("TViewPubFunctions::AddLast","Operation not allowed on a view.");
}

////////////////////////////////////////////////////////////////////////////////
/// AddAt is not allowed in this class.
/// See TList::AddAt for the intended behavior.

void TViewPubFunctions::AddAt(TObject * /* obj */, Int_t /* idx */)
{
   ::Error("TViewPubFunctions::AddAt","Operation not allowed on a view.");
}

////////////////////////////////////////////////////////////////////////////////
/// AddAfter is not allowed in this class.
/// See TList::AddAfter for the intended behavior.

void TViewPubFunctions::AddAfter(const TObject * /* after */, TObject * /* obj */)
{
   ::Error("TViewPubFunctions::RemAddLastove","Operation not allowed on a view.");
}

////////////////////////////////////////////////////////////////////////////////
/// AddAfter is not allowed in this class.
/// See TList::AddAfter for the intended behavior.

void TViewPubFunctions::AddAfter(TObjLink * /* after */, TObject * /* obj */)
{
   ::Error("TViewPubFunctions::AddAfter","Operation not allowed on a view.");
}

////////////////////////////////////////////////////////////////////////////////
/// AddBefore is not allowed in this class.
/// See TList::AddBefore for the intended behavior.

void TViewPubFunctions::AddBefore(const TObject * /* before */, TObject * /* obj */)
{
   ::Error("TViewPubFunctions::AddBefore","Operation not allowed on a view.");
}

////////////////////////////////////////////////////////////////////////////////
/// AddBefore is not allowed in this class.
/// See TList::AddBefore for the intended behavior.

void TViewPubFunctions::AddBefore(TObjLink * /* before */, TObject * /* obj */)
{
   ::Error("TViewPubFunctions::AddBefore","Operation not allowed on a view.");
}

////////////////////////////////////////////////////////////////////////////////
/// Returns the object at position idx. Returns 0 if idx is out of range.

TObject  *TViewPubFunctions::At(Int_t idx) const
{
   Int_t i = 0;
   TIter next(&fClasses);
   while (TClass *cl = (TClass*)next()) {
      TIter funcnext(cl->GetListOfMethods(kFALSE));
      while (TFunction *p = (TFunction*) funcnext()) {
         if (p->Property() & kIsPublic) {
            if (i == idx) return p;
            ++i;
         }
      }
   }
   return 0;
}

////////////////////////////////////////////////////////////////////////////////
/// After is not allowed in this class.
/// See TList::After for the intended behavior.

TObject  *TViewPubFunctions::After(const TObject * /* obj */) const
{
   ::Error("TViewPubFunctions::After","Operation not allowed on a view.");
   return 0;
}

////////////////////////////////////////////////////////////////////////////////
/// Before is not allowed in this class.
/// See TList::Before for the intended behavior.

TObject  *TViewPubFunctions::Before(const TObject * /* obj */) const
{
   ::Error("TViewPubFunctions::Before","Operation not allowed on a view.");
   return 0;
}

////////////////////////////////////////////////////////////////////////////////
/// First is not allowed in this class.
/// See TList::First for the intended behavior.

TObject  *TViewPubFunctions::First() const
{
   ::Error("TViewPubFunctions::First","Operation not allowed on a view.");
   return 0;
}

////////////////////////////////////////////////////////////////////////////////
/// FirstLink is not allowed in this class.
/// See TList::FirstLink for the intended behavior.

TObjLink *TViewPubFunctions::FirstLink() const
{
   ::Error("TViewPubFunctions::FirstLink","Operation not allowed on a view.");
   return 0;
}

////////////////////////////////////////////////////////////////////////////////
/// GetObjectRef is not allowed in this class.
/// See TList::GetObjectRef for the intended behavior.

TObject **TViewPubFunctions::GetObjectRef(const TObject * /* obj */) const
{
   ::Error("TViewPubFunctions::GetObjectRef","Operation not yet allowed on a view.");
   return 0;
}

////////////////////////////////////////////////////////////////////////////////
/// Return the total number of public methods (currently loaded in the list
/// of functions) in this class and all its base classes.

Int_t TViewPubFunctions::GetSize() const
{
   Int_t size = 0;
   TIter next(&fClasses);
   while (TClass *cl = (TClass*)next()) {
      TIter funcnext(cl->GetListOfMethods(kFALSE));
      while (TFunction *p = (TFunction*) funcnext())
         if (p->Property() & kIsPublic) ++size;
   }
   return size;

}

////////////////////////////////////////////////////////////////////////////////
/// Load all the functions known to the interpreter for the scope 'fClass'
/// and all its bases classes.

void TViewPubFunctions::Load()
{
   TIter next(&fClasses);
   while (TClass *cl = (TClass*)next()) {
      cl->GetListOfMethods(kTRUE);
   }
}

////////////////////////////////////////////////////////////////////////////////
/// Last is not allowed in this class.
/// See TList::Last for the intended behavior.

TObject  *TViewPubFunctions::Last() const
{
   ::Error("TViewPubFunctions::Last","Operation not allowed on a view.");
   return 0;
}

////////////////////////////////////////////////////////////////////////////////
/// LastLink is not allowed in this class.
/// See TList::LastLink for the intended behavior.

TObjLink *TViewPubFunctions::LastLink() const
{
   ::Error("TViewPubFunctions::LastLink","Operation not allowed on a view.");
   return 0;
}

////////////////////////////////////////////////////////////////////////////////
/// RecursiveRemove is not allowed in this class.
/// See TList::RecursiveRemove for the intended behavior.

void TViewPubFunctions::RecursiveRemove(TObject * /* obj */)
{
   ::Error("TViewPubFunctions::RecursiveRemove","Operation not allowed on a view.");
}

////////////////////////////////////////////////////////////////////////////////
/// Remove is not allowed in this class.
/// See TList::Remove for the intended behavior.

TObject   *TViewPubFunctions::Remove(TObject * /* obj */)
{
   ::Error("TViewPubFunctions::Remove","Operation not allowed on a view.");
   return 0;
}

////////////////////////////////////////////////////////////////////////////////
/// Remove is not allowed in this class.
/// See TList::Remove for the intended behavior.

TObject   *TViewPubFunctions::Remove(TObjLink * /* lnk */)
{
   ::Error("TViewPubFunctions::Remove","Operation not allowed on a view.");
   return 0;
}

/** \class TViewPubFunctionsIter
Iterator of over the view's content
*/

// ClassImp(TViewPubFunctionsIter)

////////////////////////////////////////////////////////////////////////////////
/// Create a new list iterator. By default the iteration direction
/// is kIterForward. To go backward use kIterBackward.

TViewPubFunctionsIter::TViewPubFunctionsIter(const TViewPubFunctions *l, Bool_t dir)
: fView(l),fClassIter(l->GetListOfClasses(),dir), fFuncIter((TCollection *)0),
  fStarted(kFALSE), fDirection(dir)
{
}

////////////////////////////////////////////////////////////////////////////////
/// Copy ctor.

TViewPubFunctionsIter::TViewPubFunctionsIter(const TViewPubFunctionsIter &iter) :
   TIterator(iter), fView(iter.fView),
   fClassIter(iter.fClassIter), fFuncIter(iter.fFuncIter),
   fStarted(iter.fStarted), fDirection(iter.fDirection)
{
}

////////////////////////////////////////////////////////////////////////////////
/// Overridden assignment operator.

TIterator &TViewPubFunctionsIter::operator=(const TIterator &rhs)
{
   const TViewPubFunctionsIter *iter = dynamic_cast<const TViewPubFunctionsIter*>(&rhs);
   if (this != &rhs && iter) {
      fView      = iter->fView;
      fClassIter = iter->fClassIter;
      fFuncIter  = iter->fFuncIter;
      fStarted   = iter->fStarted;
      fDirection = iter->fDirection;
   }
   return *this;
}

////////////////////////////////////////////////////////////////////////////////
/// Overloaded assignment operator.

TViewPubFunctionsIter &TViewPubFunctionsIter::operator=(const TViewPubFunctionsIter &rhs)
{
   if (this != &rhs) {
      fView      = rhs.fView;
      fClassIter = rhs.fClassIter;
      fFuncIter  = rhs.fFuncIter;
      fStarted   = rhs.fStarted;
      fDirection = rhs.fDirection;
   }
   return *this;
}

////////////////////////////////////////////////////////////////////////////////
/// Return next object in the list. Returns 0 when no more objects in list.

TObject *TViewPubFunctionsIter::Next()
{
   if (!fView) return 0;

   if (!fStarted) {
      TClass *current = (TClass*)fClassIter();
      fStarted = kTRUE;
      if (current) {
         fFuncIter.~TIter();
         new (&(fFuncIter)) TIter(current->GetListOfMethods(kFALSE),fDirection);
      } else {
         return 0;
      }
   }

   while (1) {

      TFunction *func = (TFunction *)fFuncIter();
      if (!func) {
         // End of list of functions, move to the next;
         TClass *current = (TClass*)fClassIter();
         if (current) {
            fFuncIter.~TIter();
            new (&(fFuncIter)) TIter(current->GetListOfMethods(kFALSE),fDirection);
            continue;
         } else {
            return 0;
         }
      } else if (func->Property() & kIsPublic) {
         // If it is public we found the next one.
         return func;
      }

   }
   // Not reachable.
   return 0;
}

////////////////////////////////////////////////////////////////////////////////
/// Reset list iterator.

void TViewPubFunctionsIter::Reset()
{
   fStarted = kFALSE;
   fClassIter.Reset();
}

////////////////////////////////////////////////////////////////////////////////
/// This operator compares two TIterator objects.

Bool_t TViewPubFunctionsIter::operator!=(const TIterator &aIter) const
{
   const TViewPubFunctionsIter *iter = dynamic_cast<const TViewPubFunctionsIter*>(&aIter);
   if (iter) {
      return (fClassIter != iter->fClassIter || fFuncIter != iter->fFuncIter);
   }
   return false; // for base class we don't implement a comparison
}

////////////////////////////////////////////////////////////////////////////////
/// This operator compares two TViewPubFunctionsIter objects.

Bool_t TViewPubFunctionsIter::operator!=(const TViewPubFunctionsIter &aIter) const
{
   return (fClassIter != aIter.fClassIter || fFuncIter != aIter.fFuncIter);
}

<|endoftext|>


<commit_before>#ifndef STAN_VARIATIONAL_ADVI_HPP
#define STAN_VARIATIONAL_ADVI_HPP

#include <stan/math/prim/scal/err/check_positive.hpp>

#include <stan/model/util.hpp>

#include <stan/services/io/write_iteration_csv.hpp>
#include <stan/services/io/write_iteration.hpp>
#include <stan/services/error_codes.hpp>

#include <stan/variational/families/normal_fullrank.hpp>
#include <stan/variational/families/normal_meanfield.hpp>

#include <stan/io/dump.hpp>

#include <boost/circular_buffer.hpp>
#include <ostream>
#include <limits>
#include <vector>
#include <numeric>
#include <algorithm>

namespace stan {

  namespace variational {

    /**
     * AUTOMATIC DIFFERENTIATION VARIATIONAL INFERENCE
     *
     * Runs "black box" variational inference by applying stochastic gradient
     * ascent in order to maximize the Evidence Lower Bound for a given model
     * and variational family.
     *
     * @tparam M                     class of model
     * @tparam Q                     class of variational distribution
     * @tparam BaseRNG               class of random number generator
     * @param  m                     stan model
     * @param  cont_params           initialization of continuous parameters
     * @param  n_monte_carlo_grad    number of samples for gradient computation
     * @param  n_monte_carlo_elbo    number of samples for ELBO computation
     * @param  eta_adagrad           eta parameter for adaGrad
     * @param  rng                   random number generator
     * @param  eval_elbo             evaluate ELBO at every "eval_elbo" iters
     * @param  n_posterior_samples   number of samples to draw from posterior
     * @param  print_stream          stream for convergence assessment output
     * @param  output_stream         stream for parameters output
     * @param  diagnostic_stream     stream for ELBO output
     */
    template <class M, class Q, class BaseRNG>
    class advi {
    public:
      advi(M& m,
           Eigen::VectorXd& cont_params,
           int n_monte_carlo_grad,
           int n_monte_carlo_elbo,
           double eta_adagrad,
           BaseRNG& rng,
           int eval_elbo,
           int n_posterior_samples,
           std::ostream* print_stream,
           std::ostream* output_stream,
           std::ostream* diagnostic_stream) :
        model_(m),
        cont_params_(cont_params),
        rng_(rng),
        n_monte_carlo_grad_(n_monte_carlo_grad),
        n_monte_carlo_elbo_(n_monte_carlo_elbo),
        eta_adagrad_(eta_adagrad),
        eval_elbo_(eval_elbo),
        n_posterior_samples_(n_posterior_samples),
        print_stream_(print_stream),
        out_stream_(output_stream),
        diag_stream_(diagnostic_stream) {
        static const char* function = "stan::variational::advi";
        stan::math::check_positive(function,
                                 "Number of Monte Carlo samples for gradients",
                                 n_monte_carlo_grad_);
        stan::math::check_positive(function,
                                 "Number of Monte Carlo samples for ELBO",
                                 n_monte_carlo_elbo_);
        stan::math::check_positive(function,
                                 "Number of posterior samples for output",
                                 n_posterior_samples_);
        stan::math::check_positive(function, "Eta stepsize", eta_adagrad_);
        }

      /**
       * Calculates the Evidence Lower BOund (ELBO) by sampling from
       * the variational distribution and then evaluating the log joint,
       * adjusted by the entropy term of the variational distribution.
       *
       * @tparam Q class of variational distribution
       * @return   evidence lower bound (elbo)
       */
      double calc_ELBO(const Q& variational) const {
        double elbo = 0.0;
        int dim = variational.dimension();

        Eigen::VectorXd zeta = Eigen::VectorXd::Zero(dim);

        for (int i = 0; i < n_monte_carlo_elbo_; ++i) {
          // Draw from variational distribution
          zeta = variational.sample(rng_);

          // Accumulate log probability
          elbo += (model_.template log_prob<false, true>(zeta, print_stream_));
        }

        // Divide to get Monte Carlo integral estimate
        elbo /= n_monte_carlo_elbo_;

        // Add entropy term
        elbo += variational.entropy();

        return elbo;
      }

      /**
       * Calculates the "black box" gradient of the ELBO.
       *
       * @tparam Q         class of variational distribution
       * @param  elbo_grad gradient of ELBO with respect to variational parameters
       */
      void calc_ELBO_grad(const Q& variational, Q& elbo_grad) const {
        static const char* function =
          "stan::variational::advi::calc_ELBO_grad";

        stan::math::check_size_match(function,
                        "Dimension of elbo_grad", elbo_grad.dimension(),
                        "Dimension of variational q", variational.dimension());
        stan::math::check_size_match(function,
                        "Dimension of variational q", variational.dimension(),
                        "Dimension of variables in model", cont_params_.size());

        variational.calc_grad(elbo_grad,
                              model_, cont_params_, n_monte_carlo_grad_, rng_,
                              print_stream_);
      }

      /**
       * Runs stochastic gradient ascent with Adagrad.
       *
       * @tparam Q              class of variational distribution
       * @param  tol_rel_obj    relative tolerance parameter for convergence
       * @param  max_iterations max number of iterations to run algorithm
       */
      void robbins_monro_adagrad(Q& variational,
                                 double tol_rel_obj,
                                 int max_iterations) const {
        static const char* function =
          "stan::variational::advi.robbins_monro_adagrad";

        stan::math::check_positive(function,
                                   "Relative objective function tolerance",
                                   tol_rel_obj);
        stan::math::check_positive(function,
                                   "Maximum iterations",
                                   max_iterations);

        // Gradient parameters
        Q elbo_grad = Q(model_.num_params_r());

        // Adagrad parameters
        double tau = 1.0;
        Q params_adagrad = Q(model_.num_params_r());

        // RMSprop window_size
        double window_size = 10.0;
        double post_factor = 1.0 / window_size;
        double pre_factor  = 1.0 - post_factor;

        // Initialize ELBO and convergence tracking variables
        double elbo(0.0);
        double elbo_prev      = std::numeric_limits<double>::min();
        double delta_elbo     = std::numeric_limits<double>::max();
        double delta_elbo_ave = std::numeric_limits<double>::max();
        double delta_elbo_med = std::numeric_limits<double>::max();

        // Heuristic to estimate how far to look back in rolling window
        int cb_size = static_cast<int>(
                std::max(0.1*max_iterations/static_cast<double>(eval_elbo_),
                         1.0));
        boost::circular_buffer<double> cb(cb_size);

        // Print stuff
        if (print_stream_) {
          *print_stream_ << "  iter"
                         << "       ELBO"
                         << "   delta_ELBO_mean"
                         << "   delta_ELBO_med"
                         << "   notes "
                         << std::endl;
        }

        // Timing variables
        clock_t start = clock();
        clock_t end;
        double delta_t;

        // Main loop
        std::vector<double> print_vector;
        bool do_more_iterations = true;
        int iter_counter = 0;
        while (do_more_iterations) {
          // Compute gradient using Monte Carlo integration
          calc_ELBO_grad(variational, elbo_grad);

          // RMSprop moving average weighting
          if (iter_counter == 0) {
            params_adagrad += elbo_grad.square();
          } else {
            params_adagrad = pre_factor * params_adagrad +
                             post_factor * elbo_grad.square();
          }

          // Stochastic gradient update
          variational += eta_adagrad_ * elbo_grad /
            (tau + params_adagrad.sqrt());

          // Check for convergence every "eval_elbo_"th iteration
          if (iter_counter % eval_elbo_ == 0) {
            elbo_prev = elbo;
            elbo = calc_ELBO(variational);
            delta_elbo = rel_decrease(elbo, elbo_prev);
            cb.push_back(delta_elbo);
            delta_elbo_ave = std::accumulate(cb.begin(), cb.end(), 0.0)
                             / static_cast<double>(cb.size());
            delta_elbo_med = circ_buff_median(cb);
            if (print_stream_) {
              *print_stream_
                        << "  "
                        << std::setw(4) << iter_counter
                        << "  "
                        << std::right << std::setw(9) << std::setprecision(1)
                        << elbo
                        << "  "
                        << std::setw(16) << std::fixed << std::setprecision(3)
                        << delta_elbo_ave
                        << "  "
                        << std::setw(15) << std::fixed << std::setprecision(3)
                        << delta_elbo_med;
            }

            if (diag_stream_) {
              end = clock();
              delta_t = static_cast<double>(end - start) / CLOCKS_PER_SEC;

              print_vector.clear();
              print_vector.push_back(delta_t);
              print_vector.push_back(elbo);
              services::io::write_iteration_csv(*diag_stream_,
                                                iter_counter, print_vector);
            }

            if (delta_elbo_ave < tol_rel_obj) {
              if (print_stream_)
                *print_stream_ << "   MEAN ELBO CONVERGED";
              do_more_iterations = false;
            }

            if (delta_elbo_med < tol_rel_obj) {
              if (print_stream_)
                *print_stream_ << "   MEDIAN ELBO CONVERGED";
              do_more_iterations = false;
            }

            if (iter_counter > 100) {
              if (delta_elbo_med > 0.5 || delta_elbo_ave > 0.5) {
                if (print_stream_)
                  *print_stream_ << "   MAY BE DIVERGING... INSPECT ELBO";
              }
            }

            if (print_stream_)
              *print_stream_ << std::endl;
          }

          // Check for max iterations
          if (iter_counter == max_iterations) {
            if (print_stream_)
              *print_stream_ << "MAX ITERATIONS REACHED" << std::endl;
            do_more_iterations = false;
          }

          ++iter_counter;
        }
      }

      /**
       * Runs the algorithm and writes to output.
       *
       * @param  tol_rel_obj    relative tolerance parameter for convergence
       * @param  max_iterations max number of iterations to run algorithm
       */
      int run(double tol_rel_obj, int max_iterations) const {
        if (diag_stream_) {
          *diag_stream_ << "iter,time_in_seconds,ELBO" << std::endl;
        }

        // initialize variational approximation
        Q variational = Q(cont_params_);

        // run inference algorithm
        robbins_monro_adagrad(variational, tol_rel_obj, max_iterations);

        // get mean of posterior approximation and write on first output line
        cont_params_ = variational.mean();
        std::vector<double> cont_vector(cont_params_.size());
        for (int i = 0; i < cont_params_.size(); ++i)
          cont_vector.at(i) = cont_params_(i);
        std::vector<int> disc_vector;

        if (out_stream_) {
          services::io::write_iteration(*out_stream_, model_, rng_,
                                        0.0, cont_vector, disc_vector,
                                        print_stream_);
        }

        // draw more samples from posterior and write on subsequent lines
        if (out_stream_) {
          for (int n = 0; n < n_posterior_samples_; ++n) {
            cont_params_ = variational.sample(rng_);
            for (int i = 0; i < cont_params_.size(); ++i)
              cont_vector.at(i) = cont_params_(i);
            services::io::write_iteration(*out_stream_, model_, rng_,
                          0.0, cont_vector, disc_vector, print_stream_);
          }
        }

        return stan::services::error_codes::OK;
      }

      // Helper function: compute the median of a circular buffer
      double circ_buff_median(const boost::circular_buffer<double>& cb) const {
          // FIXME: naive implementation; creates a copy as a vector
          std::vector<double> v;
          for (boost::circular_buffer<double>::const_iterator i = cb.begin();
                i != cb.end(); ++i) {
            v.push_back(*i);
          }

          size_t n = v.size() / 2;
          std::nth_element(v.begin(), v.begin()+n, v.end());
          return v[n];
      }

      // Helper function: compute relative decrease between two doubles
      double rel_decrease(double prev, double curr) const {
        return std::abs(curr - prev) / std::abs(prev);
      }

    protected:
      M& model_;
      Eigen::VectorXd& cont_params_;
      BaseRNG& rng_;
      int n_monte_carlo_grad_;
      int n_monte_carlo_elbo_;
      double eta_adagrad_;
      int eval_elbo_;
      int n_posterior_samples_;
      std::ostream* print_stream_;
      std::ostream* out_stream_;
      std::ostream* diag_stream_;
    };
  }  // variational
}  // stan

#endif
<commit_msg>evaluate lp when writing to output<commit_after>#ifndef STAN_VARIATIONAL_ADVI_HPP
#define STAN_VARIATIONAL_ADVI_HPP

#include <stan/math/prim/scal/err/check_positive.hpp>

#include <stan/model/util.hpp>

#include <stan/services/io/write_iteration_csv.hpp>
#include <stan/services/io/write_iteration.hpp>
#include <stan/services/error_codes.hpp>

#include <stan/variational/families/normal_fullrank.hpp>
#include <stan/variational/families/normal_meanfield.hpp>

#include <stan/io/dump.hpp>

#include <boost/circular_buffer.hpp>
#include <ostream>
#include <limits>
#include <vector>
#include <numeric>
#include <algorithm>

namespace stan {

  namespace variational {

    /**
     * AUTOMATIC DIFFERENTIATION VARIATIONAL INFERENCE
     *
     * Runs "black box" variational inference by applying stochastic gradient
     * ascent in order to maximize the Evidence Lower Bound for a given model
     * and variational family.
     *
     * @tparam M                     class of model
     * @tparam Q                     class of variational distribution
     * @tparam BaseRNG               class of random number generator
     * @param  m                     stan model
     * @param  cont_params           initialization of continuous parameters
     * @param  n_monte_carlo_grad    number of samples for gradient computation
     * @param  n_monte_carlo_elbo    number of samples for ELBO computation
     * @param  eta_adagrad           eta parameter for adaGrad
     * @param  rng                   random number generator
     * @param  eval_elbo             evaluate ELBO at every "eval_elbo" iters
     * @param  n_posterior_samples   number of samples to draw from posterior
     * @param  print_stream          stream for convergence assessment output
     * @param  output_stream         stream for parameters output
     * @param  diagnostic_stream     stream for ELBO output
     */
    template <class M, class Q, class BaseRNG>
    class advi {
    public:
      advi(M& m,
           Eigen::VectorXd& cont_params,
           int n_monte_carlo_grad,
           int n_monte_carlo_elbo,
           double eta_adagrad,
           BaseRNG& rng,
           int eval_elbo,
           int n_posterior_samples,
           std::ostream* print_stream,
           std::ostream* output_stream,
           std::ostream* diagnostic_stream) :
        model_(m),
        cont_params_(cont_params),
        rng_(rng),
        n_monte_carlo_grad_(n_monte_carlo_grad),
        n_monte_carlo_elbo_(n_monte_carlo_elbo),
        eta_adagrad_(eta_adagrad),
        eval_elbo_(eval_elbo),
        n_posterior_samples_(n_posterior_samples),
        print_stream_(print_stream),
        out_stream_(output_stream),
        diag_stream_(diagnostic_stream) {
        static const char* function = "stan::variational::advi";
        stan::math::check_positive(function,
                                 "Number of Monte Carlo samples for gradients",
                                 n_monte_carlo_grad_);
        stan::math::check_positive(function,
                                 "Number of Monte Carlo samples for ELBO",
                                 n_monte_carlo_elbo_);
        stan::math::check_positive(function,
                                 "Number of posterior samples for output",
                                 n_posterior_samples_);
        stan::math::check_positive(function, "Eta stepsize", eta_adagrad_);
        }

      /**
       * Calculates the Evidence Lower BOund (ELBO) by sampling from
       * the variational distribution and then evaluating the log joint,
       * adjusted by the entropy term of the variational distribution.
       *
       * @tparam Q class of variational distribution
       * @return   evidence lower bound (elbo)
       */
      double calc_ELBO(const Q& variational) const {
        double elbo = 0.0;
        int dim = variational.dimension();

        Eigen::VectorXd zeta(dim);

        for (int i = 0; i < n_monte_carlo_elbo_; ++i) {
          zeta = variational.sample(rng_);
          // Accumulate log probability
          elbo += model_.template log_prob<false, true>(zeta, print_stream_);
        }

        // Divide to get Monte Carlo integral estimate
        elbo /= n_monte_carlo_elbo_;
        elbo += variational.entropy();

        return elbo;
      }

      /**
       * Calculates the "black box" gradient of the ELBO.
       *
       * @tparam Q         class of variational distribution
       * @param  elbo_grad gradient of ELBO with respect to variational parameters
       */
      void calc_ELBO_grad(const Q& variational, Q& elbo_grad) const {
        static const char* function =
          "stan::variational::advi::calc_ELBO_grad";

        stan::math::check_size_match(function,
                        "Dimension of elbo_grad", elbo_grad.dimension(),
                        "Dimension of variational q", variational.dimension());
        stan::math::check_size_match(function,
                        "Dimension of variational q", variational.dimension(),
                        "Dimension of variables in model", cont_params_.size());

        variational.calc_grad(elbo_grad,
                              model_, cont_params_, n_monte_carlo_grad_, rng_,
                              print_stream_);
      }

      /**
       * Runs stochastic gradient ascent with Adagrad.
       *
       * @tparam Q              class of variational distribution
       * @param  tol_rel_obj    relative tolerance parameter for convergence
       * @param  max_iterations max number of iterations to run algorithm
       */
      void robbins_monro_adagrad(Q& variational,
                                 double tol_rel_obj,
                                 int max_iterations) const {
        static const char* function =
          "stan::variational::advi.robbins_monro_adagrad";

        stan::math::check_positive(function,
                                   "Relative objective function tolerance",
                                   tol_rel_obj);
        stan::math::check_positive(function,
                                   "Maximum iterations",
                                   max_iterations);

        // Gradient parameters
        Q elbo_grad = Q(model_.num_params_r());

        // Adagrad parameters
        double tau = 1.0;
        Q params_adagrad = Q(model_.num_params_r());

        // RMSprop window_size
        double window_size = 10.0;
        double post_factor = 1.0 / window_size;
        double pre_factor  = 1.0 - post_factor;

        // Initialize ELBO and convergence tracking variables
        double elbo(0.0);
        double elbo_prev      = std::numeric_limits<double>::min();
        double delta_elbo     = std::numeric_limits<double>::max();
        double delta_elbo_ave = std::numeric_limits<double>::max();
        double delta_elbo_med = std::numeric_limits<double>::max();

        // Heuristic to estimate how far to look back in rolling window
        int cb_size = static_cast<int>(
                std::max(0.1*max_iterations/static_cast<double>(eval_elbo_),
                         1.0));
        boost::circular_buffer<double> cb(cb_size);

        // Print stuff
        if (print_stream_) {
          *print_stream_ << "  iter"
                         << "       ELBO"
                         << "   delta_ELBO_mean"
                         << "   delta_ELBO_med"
                         << "   notes "
                         << std::endl;
        }

        // Timing variables
        clock_t start = clock();
        clock_t end;
        double delta_t;

        // Main loop
        std::vector<double> print_vector;
        bool do_more_iterations = true;
        int iter_counter = 0;
        while (do_more_iterations) {
          // Compute gradient using Monte Carlo integration
          calc_ELBO_grad(variational, elbo_grad);

          // RMSprop moving average weighting
          if (iter_counter == 0) {
            params_adagrad += elbo_grad.square();
          } else {
            params_adagrad = pre_factor * params_adagrad +
                             post_factor * elbo_grad.square();
          }

          // Stochastic gradient update
          variational += eta_adagrad_ * elbo_grad /
            (tau + params_adagrad.sqrt());

          // Check for convergence every "eval_elbo_"th iteration
          if (iter_counter % eval_elbo_ == 0) {
            elbo_prev = elbo;
            elbo = calc_ELBO(variational);
            delta_elbo = rel_decrease(elbo, elbo_prev);
            cb.push_back(delta_elbo);
            delta_elbo_ave = std::accumulate(cb.begin(), cb.end(), 0.0)
                             / static_cast<double>(cb.size());
            delta_elbo_med = circ_buff_median(cb);
            if (print_stream_) {
              *print_stream_
                        << "  "
                        << std::setw(4) << iter_counter
                        << "  "
                        << std::right << std::setw(9) << std::setprecision(1)
                        << elbo
                        << "  "
                        << std::setw(16) << std::fixed << std::setprecision(3)
                        << delta_elbo_ave
                        << "  "
                        << std::setw(15) << std::fixed << std::setprecision(3)
                        << delta_elbo_med;
            }

            if (diag_stream_) {
              end = clock();
              delta_t = static_cast<double>(end - start) / CLOCKS_PER_SEC;

              print_vector.clear();
              print_vector.push_back(delta_t);
              print_vector.push_back(elbo);
              services::io::write_iteration_csv(*diag_stream_,
                                                iter_counter, print_vector);
            }

            if (delta_elbo_ave < tol_rel_obj) {
              if (print_stream_)
                *print_stream_ << "   MEAN ELBO CONVERGED";
              do_more_iterations = false;
            }

            if (delta_elbo_med < tol_rel_obj) {
              if (print_stream_)
                *print_stream_ << "   MEDIAN ELBO CONVERGED";
              do_more_iterations = false;
            }

            if (iter_counter > 100) {
              if (delta_elbo_med > 0.5 || delta_elbo_ave > 0.5) {
                if (print_stream_)
                  *print_stream_ << "   MAY BE DIVERGING... INSPECT ELBO";
              }
            }

            if (print_stream_)
              *print_stream_ << std::endl;
          }

          // Check for max iterations
          if (iter_counter == max_iterations) {
            if (print_stream_)
              *print_stream_ << "MAX ITERATIONS REACHED" << std::endl;
            do_more_iterations = false;
          }

          ++iter_counter;
        }
      }

      /**
       * Runs the algorithm and writes to output.
       *
       * @param  tol_rel_obj    relative tolerance parameter for convergence
       * @param  max_iterations max number of iterations to run algorithm
       */
      int run(double tol_rel_obj, int max_iterations) const {
        if (diag_stream_) {
          *diag_stream_ << "iter,time_in_seconds,ELBO" << std::endl;
        }

        // initialize variational approximation
        Q variational = Q(cont_params_);

        // run inference algorithm
        robbins_monro_adagrad(variational, tol_rel_obj, max_iterations);

        // get mean of posterior approximation and write on first output line
        cont_params_ = variational.mean();
        // This is temporary as lp is not really helpful for variational
        // inference; furthermore it can be costly to compute.
        double lp = model_.template log_prob<false, true>(cont_params_,
          print_stream_);
        std::vector<double> cont_vector(cont_params_.size());
        for (int i = 0; i < cont_params_.size(); ++i)
          cont_vector.at(i) = cont_params_(i);
        std::vector<int> disc_vector;

        if (out_stream_) {
          services::io::write_iteration(*out_stream_, model_, rng_,
                                        lp, cont_vector, disc_vector,
                                        print_stream_);
        }

        // draw more samples from posterior and write on subsequent lines
        if (out_stream_) {
          for (int n = 0; n < n_posterior_samples_; ++n) {
            cont_params_ = variational.sample(rng_);
            double lp = model_.template log_prob<false, true>(cont_params_,
              print_stream_);
            for (int i = 0; i < cont_params_.size(); ++i) {
              cont_vector.at(i) = cont_params_(i);
            }
            services::io::write_iteration(*out_stream_, model_, rng_,
                          lp, cont_vector, disc_vector, print_stream_);
          }
        }

        return stan::services::error_codes::OK;
      }

      // Helper function: compute the median of a circular buffer
      double circ_buff_median(const boost::circular_buffer<double>& cb) const {
          // FIXME: naive implementation; creates a copy as a vector
          std::vector<double> v;
          for (boost::circular_buffer<double>::const_iterator i = cb.begin();
                i != cb.end(); ++i) {
            v.push_back(*i);
          }

          size_t n = v.size() / 2;
          std::nth_element(v.begin(), v.begin()+n, v.end());
          return v[n];
      }

      // Helper function: compute relative decrease between two doubles
      double rel_decrease(double prev, double curr) const {
        return std::abs(curr - prev) / std::abs(prev);
      }

    protected:
      M& model_;
      Eigen::VectorXd& cont_params_;
      BaseRNG& rng_;
      int n_monte_carlo_grad_;
      int n_monte_carlo_elbo_;
      double eta_adagrad_;
      int eval_elbo_;
      int n_posterior_samples_;
      std::ostream* print_stream_;
      std::ostream* out_stream_;
      std::ostream* diag_stream_;
    };
  }  // variational
}  // stan

#endif
<|endoftext|>