DLVHEX_NAMESPACE_USE

BOOST_AUTO_TEST_CASE(testDisj) 
{
  ProgramCtx ctx;
  ctx.setupRegistry(RegistryPtr(new Registry));

  std::stringstream ss;
  ss <<
		// a <-(+)-> a (head/head = disjunctive)
    "a v b." << std::endl <<
    "a v c." << std::endl;
  InputProviderPtr ip(new InputProvider);
  ip->addStreamInput(ss, "testinput");
  ModuleHexParser parser;
  BOOST_REQUIRE_NO_THROW(parser.parse(ip, ctx));

	LOG_REGISTRY_PROGRAM(ctx);

  ID ida = ctx.registry()->ogatoms.getIDByString("a");
  ID idb = ctx.registry()->ogatoms.getIDByString("b");
  ID idc = ctx.registry()->ogatoms.getIDByString("c");
  BOOST_REQUIRE((ida | idb | idc) != ID_FAIL);

  // smaller more efficient dependency graph
  {
    DependencyGraph depgraph(ctx, ctx.registry());
    std::vector<ID> auxRules;
    depgraph.createDependencies(ctx.idb, auxRules);

    BOOST_CHECK_EQUAL(depgraph.countNodes(), 2);
    BOOST_CHECK_EQUAL(depgraph.countDependencies(), 2);

    // TODO test dependencies (will do manually with graphviz at the moment)

    const char* fnamev = "testDependencyGraphDisjVerbose.dot";
    LOG(INFO,"dumping verbose graph to " << fnamev);
    std::ofstream filev(fnamev);
    depgraph.writeGraphViz(filev, true);
    makeGraphVizPdf(fnamev);

    const char* fnamet = "testDependencyGraphDisjTerse.dot";
    LOG(INFO,"dumping terse graph to " << fnamet);
    std::ofstream filet(fnamet);
    depgraph.writeGraphViz(filet, false);
    makeGraphVizPdf(fnamet);
  }
}
int main(int argn, char** argv)
{
  try
  {

  DLVHEX_BENCHMARK_REGISTER_AND_START(sidoverall, "overall timing");
  
  if( argn != 5 )
  {
    std::cerr << "usage: " << argv[0] << " <heurimode> <mbmode> <backend> <inputfile>" << std::endl;
    return -1;
  }

  //
  // setup benchmarking
  //
  benchmark::BenchmarkController& ctr =
    benchmark::BenchmarkController::Instance();
  ctr.setOutput(&std::cerr);
  // for continuous statistics output, display every 1000'th output
  ctr.setPrintInterval(999);
  // deconstruct benchmarking (= output results) at scope exit 
  int dummy; // this is needed, as SCOPE_EXIT is not defined for no arguments
  BOOST_SCOPE_EXIT( (dummy) ) {
	  (void)dummy;
	  benchmark::BenchmarkController::finish();
  }
  BOOST_SCOPE_EXIT_END

  //
  // preprocess arguments
  //
  const std::string heurimode(argv[1]);
  const std::string mbmode(argv[2]);
  const std::string backend(argv[3]);
  const std::string fname(argv[4]);

  // get input
  InputProviderPtr ip(new InputProvider);
  ip->addFileInput(fname);

  // don't rewrite

  // prepare program context
  ProgramCtx ctx;
  {
    RegistryPtr registry(new Registry);
    ctx.setupRegistry(registry);
    PluginContainerPtr pluginContainer(new PluginContainer);
    ctx.setupPluginContainer(pluginContainer);
  }

  // create all testing plugin atoms
  ctx.addPluginAtom(PluginAtomPtr(new AbovePluginAtom));
  ctx.addPluginAtom(PluginAtomPtr(new SenseNotArmed1PluginAtom));
  ctx.addPluginAtom(PluginAtomPtr(new SenseNotArmed2PluginAtom));
  ctx.addPluginAtom(PluginAtomPtr(new GenPluginAtom2("gen2",2)));

  // parse HEX program
  LOG(INFO,"parsing HEX program");
  DLVHEX_BENCHMARK_REGISTER_AND_START(sidhexparse, "HexParser::parse");
  ModuleHexParser parser;
  parser.parse(ip, ctx);
  DLVHEX_BENCHMARK_STOP(sidhexparse);

  ctx.associateExtAtomsWithPluginAtoms(ctx.idb,true);

  //LOG_REGISTRY_PROGRAM(ctx);

  // create dependency graph
  LOG(INFO,"creating dependency graph");
  DLVHEX_BENCHMARK_REGISTER_AND_START(siddepgraph, "create dependencygraph");
  std::vector<dlvhex::ID> auxRules;
  dlvhex::DependencyGraph depgraph(ctx, ctx.registry());
  depgraph.createDependencies(ctx.idb, auxRules);
  DLVHEX_BENCHMARK_STOP(siddepgraph);
  #ifndef NDEBUG
  writeGraphViz(depgraph, fname+"PlainHEXDepGraph");
  #endif

  // create component graph
  LOG(INFO,"creating component graph");
  DLVHEX_BENCHMARK_REGISTER_AND_START(sidcompgraph, "create componentgraph");
  dlvhex::ComponentGraph compgraph(depgraph, ctx.registry());
  DLVHEX_BENCHMARK_STOP(sidcompgraph);
  #ifndef NDEBUG
  writeGraphViz(compgraph, fname+"PlainHEXCompGraph");
  #endif

  // manage external evaluation configuration / backend
  ASPSolverManager::SoftwareConfigurationPtr externalEvalConfig;
  if( backend == "dlv" )
  {
    externalEvalConfig.reset(new ASPSolver::DLVSoftware::Configuration);
  }
  else if( backend == "libdlv" )
  {
    #ifdef HAVE_LIBDLV
    externalEvalConfig.reset(new ASPSolver::DLVLibSoftware::Configuration);
    #else
    std::cerr << "sorry, libdlv not compiled in" << std::endl;
    return -1;
    #endif // HAVE_LIBDLV
  }
  else if( backend == "libclingo" )
  {
    #ifdef HAVE_LIBCLINGO
    externalEvalConfig.reset(new ASPSolver::ClingoSoftware::Configuration);
    #else
    std::cerr << "sorry, libclingo not compiled in" << std::endl;
    return -1;
    #endif // HAVE_LIBCLINGO
  }
  else
  {
    std::cerr << "usage: <backend> must be one of 'dlv','libdlv','libclingo'" << std::endl;
    return -1;
  }

  // create eval graph
  LOG(INFO,"creating eval graph");
  DLVHEX_BENCHMARK_REGISTER_AND_START(sidevalgraph, "create evalgraph");
  FinalEvalGraph evalgraph;
  EvalGraphBuilder egbuilder(ctx, compgraph, evalgraph, externalEvalConfig);

  // use one of several heuristics
  if( heurimode == "old" )
  {
    // old DLVHEX heuristic
    LOG(INFO,"building eval graph with old heuristics");
    EvalHeuristicOldDlvhex heuristicOldDlvhex;
    heuristicOldDlvhex.build(egbuilder);
  }
  else if( heurimode == "trivial" )
  {
    // trivial heuristic: just take component graph
    // (maximum number of eval units, probably large overhead)
    LOG(INFO,"building eval graph with trivial heuristics");
    EvalHeuristicTrivial heuristic;
    heuristic.build(egbuilder);
  }
  else if( heurimode == "easy" )
  {
    // easy heuristic: just make some easy adjustments to improve on the trivial heuristics
    LOG(INFO,"building eval graph with easy heuristics");
    EvalHeuristicEasy heuristic;
    heuristic.build(egbuilder);
  }
  else
  {
    std::cerr << "usage: <heurimode> must be one of 'old','trivial','easy'" << std::endl;
    return -1;
  }
  DLVHEX_BENCHMARK_STOP(sidevalgraph);

  #ifndef NDEBUG
  writeGraphViz(compgraph, fname+"PlainHEXEvalGraph");
  #endif

  // setup final unit
  LOG(INFO,"setting up final unit");
  DLVHEX_BENCHMARK_REGISTER_AND_START(sidfinalunit, "creating final unit");
  EvalUnit ufinal;
  {
    ufinal = evalgraph.addUnit(FinalEvalGraph::EvalUnitPropertyBundle());
    LOG(INFO,"ufinal = " << ufinal);

    FinalEvalGraph::EvalUnitIterator it, itend;
    boost::tie(it, itend) = evalgraph.getEvalUnits();
    for(; it != itend && *it != ufinal; ++it)
    {
      DBGLOG(DBG,"adding dependency from ufinal to unit " << *it << " join order " << *it);
      // we can do this because we know that eval units (= vertices of a vecS adjacency list) are unsigned integers
      evalgraph.addDependency(ufinal, *it, FinalEvalGraph::EvalUnitDepPropertyBundle(*it));
    }
  }
  DLVHEX_BENCHMARK_STOP(sidfinalunit);

  // prepare for output
  //GenericOutputBuilder ob;
  #warning reactivate and redesign outputbuilder

  //std::cerr << __FILE__ << ":" << __LINE__ << std::endl << *ctx.registry() << std::endl;

  // evaluate
  LOG(INFO,"evaluating");
  DLVHEX_BENCHMARK_REGISTER(sidoutputmodel, "output model");
  dlvhex::ModelBuilderConfig<FinalEvalGraph> mbcfg(evalgraph);
  if( mbmode == "online" )
  {
    typedef FinalOnlineModelBuilder::Model Model;
    typedef FinalOnlineModelBuilder::OptionalModel OptionalModel;
    typedef FinalOfflineModelBuilder::MyModelGraph MyModelGraph;
    LOG(INFO,"creating model builder");
    DLVHEX_BENCHMARK_REGISTER_AND_START(sidonlinemb, "create online mb");

    FinalOnlineModelBuilder mb(mbcfg);
    DLVHEX_BENCHMARK_STOP(sidonlinemb);

    // get and print all models
    OptionalModel m;
    DLVHEX_BENCHMARK_REGISTER(sidgetnextonlinemodel, "get next online model");
    unsigned mcount = 0;
    do
    {
      DBGLOG(DBG,"requesting model");
      DLVHEX_BENCHMARK_START(sidgetnextonlinemodel);
      m = mb.getNextIModel(ufinal);
      DLVHEX_BENCHMARK_STOP(sidgetnextonlinemodel);
      if( !!m )
      {
        InterpretationConstPtr interpretation =
          mb.getModelGraph().propsOf(m.get()).interpretation;
        #ifndef NDEBUG
        DBGLOG(DBG,"got model#" << mcount << ":" << *interpretation);
        std::set<Model> onlyFor;
        onlyFor.insert(m.get());
        GraphVizFunc func = boost::bind(&writeEgMgGraphViz<MyModelGraph>, _1,
            true, boost::cref(mb.getEvalGraph()), boost::cref(mb.getModelGraph()), onlyFor);
        std::stringstream smodel;
        smodel << fname << "PlainHEXOnlineModel" << mcount;
        writeGraphVizFunctors(func, func, smodel.str());
        #endif
        mcount++;

        // output model
        {
          std::cout << *interpretation << std::endl;
        }
        //std::cerr << __FILE__ << ":" << __LINE__ << std::endl << *ctx.registry() << std::endl;

        #ifndef NDEBUG
        mb.printEvalGraphModelGraph(std::cerr);
        #endif
      }
    }
    while( !!m );
    #ifndef NDEBUG
    mb.printEvalGraphModelGraph(std::cerr);
    #endif
    #ifndef NDEBUG
		GraphVizFunc func = boost::bind(&writeEgMgGraphViz<MyModelGraph>, _1,
				true, boost::cref(mb.getEvalGraph()), boost::cref(mb.getModelGraph()), boost::none);
		writeGraphVizFunctors(func, func, fname+"PlainHEXOnlineEgMg");
    #endif
    //std::cerr << __FILE__ << ":" << __LINE__ << std::endl << *ctx.registry() << std::endl;

    DLVHEX_BENCHMARK_STOP(sidoverall);
    std::cerr << "TIMING " << fname << " " << heurimode << " " << mbmode << " " << backend << " " <<
      evalgraph.countEvalUnits() << " evalunits " << evalgraph.countEvalUnitDeps() << " evalunitdeps " << mcount << " models ";
    benchmark::BenchmarkController::Instance().printDuration(std::cerr, sidoverall) << std::endl;
  }
  else if( mbmode == "offline" )
  {
    typedef FinalOfflineModelBuilder::Model Model;
    typedef FinalOfflineModelBuilder::OptionalModel OptionalModel;
    typedef FinalOfflineModelBuilder::MyModelGraph MyModelGraph;

    LOG(INFO,"creating model builder");
    DLVHEX_BENCHMARK_REGISTER_AND_START(sidofflinemb, "create offline mb");
    FinalOfflineModelBuilder mb(mbcfg);
    DLVHEX_BENCHMARK_STOP(sidofflinemb);

    LOG(INFO,"creating all final imodels");
    DLVHEX_BENCHMARK_REGISTER_AND_START(sidofflinemodels, "create offline models");
    mb.buildIModelsRecursively(ufinal);
    DLVHEX_BENCHMARK_STOP(sidofflinemodels);
    #ifndef NDEBUG
    mb.printEvalGraphModelGraph(std::cerr);
    #endif

    LOG(INFO,"printing models");
    DLVHEX_BENCHMARK_REGISTER_AND_START(sidprintoffmodels, "print offline models");
    MyModelGraph& mg = mb.getModelGraph();
    const MyModelGraph::ModelList& models = mg.modelsAt(ufinal, MT_IN);
    unsigned mcount = 0;

    BOOST_FOREACH(Model m, models)
    {
      InterpretationConstPtr interpretation =
        mg.propsOf(m).interpretation;
      #ifndef NDEBUG
      DBGLOG(DBG,"got model#" << mcount << ":" << *interpretation);
      std::set<Model> onlyFor;
      onlyFor.insert(m);
      GraphVizFunc func = boost::bind(&writeEgMgGraphViz<MyModelGraph>, _1,
          true, boost::cref(mb.getEvalGraph()), boost::cref(mb.getModelGraph()), onlyFor);
      std::stringstream smodel;
      smodel << fname << "PlainHEXOfflineModel" << mcount;
      writeGraphVizFunctors(func, func, smodel.str());
      #endif
      mcount++;

      // output model
      {
        std::cout << *interpretation << std::endl;
      }
    }
    DLVHEX_BENCHMARK_STOP(sidprintoffmodels);
    #ifndef NDEBUG
		GraphVizFunc func = boost::bind(&writeEgMgGraphViz<MyModelGraph>, _1,
				true, boost::cref(mb.getEvalGraph()), boost::cref(mb.getModelGraph()), boost::none);
		writeGraphVizFunctors(func, func, fname+"PlainHEXOfflineEgMg");
    #endif

    DLVHEX_BENCHMARK_STOP(sidoverall);
    std::cerr << "TIMING " << fname << " " << heurimode << " " << mbmode << " " << backend << " " <<
      evalgraph.countEvalUnits() << " evalunits " << evalgraph.countEvalUnitDeps() << " evalunitdeps " << mcount << " models ";
    benchmark::BenchmarkController::Instance().printDuration(std::cerr, sidoverall) << "s" << std::endl;
  }
  else
  {
    std::cerr << "usage: <mbmode> must be one of 'online','offline'" << std::endl;
    return -1;
  }
  return 0;
  
  }
Beispiel #3
0
void SimulatorAtom::retrieve(const Query& query, Answer& answer) throw (PluginError){

	RegistryPtr reg = query.interpretation->getRegistry();

	const Tuple& params = query.input;

	// get ASP filename
	std::string programpath = reg->terms.getByID(params[0]).getUnquotedString();

	// if we access this file for the first time, parse the content
	if (programs.find(programpath) == programs.end()){
		DBGLOG(DBG, "Parsing simulation program");
		InputProviderPtr ip(new InputProvider());
		ip->addFileInput(programpath);
		Logger::Levels l = Logger::Instance().getPrintLevels();	// workaround: verbose causes the parse call below to fail (registry pointer is 0)
		Logger::Instance().setPrintLevels(0);
		programs[programpath].changeRegistry(reg);
		ModuleHexParser hp;
		hp.parse(ip, programs[programpath]);
		Logger::Instance().setPrintLevels(l);
	}
	ProgramCtx& pc = programs[programpath];

	// construct edb
	DBGLOG(DBG, "Constructing EDB");
	InterpretationPtr edb = InterpretationPtr(new Interpretation(*pc.edb));

	// go through all input atoms
	DBGLOG(DBG, "Rewriting input");
	for(Interpretation::Storage::enumerator it =
	    query.interpretation->getStorage().first();
	    it != query.interpretation->getStorage().end(); ++it){

		ID ogid(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG, *it);
		const OrdinaryAtom& ogatom = reg->ogatoms.getByID(ogid);

		// check if the predicate matches any of the input parameters to simulator atom
		bool found = false;
		for (int inp = 1; inp < params.size(); ++inp){
			if (ogatom.tuple[0] == params[inp]){
				// replace the predicate by "in[inp]"
				std::stringstream inPredStr;
				inPredStr << "in" << inp;
				Term inPredTerm(ID::MAINKIND_TERM | ID::SUBKIND_TERM_CONSTANT, inPredStr.str());
				ID inPredID = reg->storeTerm(inPredTerm);
				OrdinaryAtom oareplace = ogatom;
				oareplace.tuple[0] = inPredID;

				// get ID of replaced atom
				ID oareplaceID = reg->storeOrdinaryGAtom(oareplace);

				// set this atom in the input interpretation
				edb->getStorage().set_bit(oareplaceID.address);
				found = true;
				break;
			}
		}
		assert(found);
	}

	DBGLOG(DBG, "Grounding simulation program");
	OrdinaryASPProgram program(pc.registry(), pc.idb, edb);
	InternalGrounderPtr ig = InternalGrounderPtr(new InternalGrounder(pc, program));
	OrdinaryASPProgram gprogram = ig->getGroundProgram();

	DBGLOG(DBG, "Evaluating simulation program");
	GenuineSolverPtr igas = GenuineSolver::getInstance(pc, gprogram);
	InterpretationPtr as = igas->getNextModel();
	if (as != InterpretationPtr()){

		// extract parameters from all atoms over predicate "out"
		DBGLOG(DBG, "Rewrting output");
		Term outPredTerm(ID::MAINKIND_TERM | ID::SUBKIND_TERM_CONSTANT, "out");
		ID outPredID = reg->storeTerm(outPredTerm);
		for(Interpretation::Storage::enumerator it =
		    as->getStorage().first();
		    it != as->getStorage().end(); ++it){

			ID ogid(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG, *it);
			const OrdinaryAtom& ogatom = reg->ogatoms.getByID(ogid);

			if (ogatom.tuple[0] == outPredID){
				Tuple t;
				for (int ot = 1; ot < ogatom.tuple.size(); ++ot){
					t.push_back(ogatom.tuple[ot]);
				}
				answer.get().push_back(t);
			}
		}
	}
}
Beispiel #4
0
void
ArgSemExtAtom::retrieve(const Query& query, Answer& answer) throw (PluginError)
{
  assert(query.input.size() == 6);

  RegistryPtr reg = getRegistry();

  // check if pspoil is true
  {
    // id of constant of saturate/spoil predicate
    ID saturate_pred = query.input[4];

    // get id of 0-ary atom
    OrdinaryAtom saturate_oatom(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG);
    saturate_oatom.tuple.push_back(saturate_pred);
    ID saturate_atom = reg->storeOrdinaryGAtom(saturate_oatom);
    DBGLOG(DBG,"got saturate_pred=" << saturate_pred << " and saturate_atom=" << saturate_atom);

    // check if atom <saturate_pred> is true in interpretation
    bool saturate = query.interpretation->getFact(saturate_atom.address);
    LOG(DBG,"ArgSemExtAtom called with pos saturate=" << saturate);

    if( saturate )
    {
      // always return true
      answer.get().push_back(Tuple());
      return;
    }
  }

  // check if nspoil is true
  {
    // id of constant of saturate/spoil predicate
    ID saturate_pred = query.input[5];

    // get id of 0-ary atom
    OrdinaryAtom saturate_oatom(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG);
    saturate_oatom.tuple.push_back(saturate_pred);
    ID saturate_atom = reg->storeOrdinaryGAtom(saturate_oatom);
    DBGLOG(DBG,"got saturate_pred=" << saturate_pred << " and saturate_atom=" << saturate_atom);

    // check if atom <saturate_pred> is true in interpretation
    bool saturate = query.interpretation->getFact(saturate_atom.address);
    LOG(DBG,"ArgSemExtAtom called with neg saturate=" << saturate);

    if( saturate )
    {
      // always return false
      answer.use();
      return;
    }
  }

  // get arguments
  const std::string& semantics = reg->getTermStringByID(query.input[0]);
  ID argRelId = query.input[1];
  ID attRelId = query.input[2];
  ID extRelId = query.input[3];

  // assemble facts from input
  std::stringstream s;
  {
    // add argumentation framework (att, arg) as predicates att/2 and arg/1
    // (ignore predicate name of given atoms)

    // TODO: we could do this more efficiently using extctx.edb->setFact(...); and not by parsing

    // arguments
    {
      PredicateMask& argMask = getPredicateMask(argRelId, reg);
      argMask.updateMask();
      InterpretationPtr argInt(new Interpretation(*query.interpretation));
      argInt->bit_and(*argMask.mask());
      for(auto begend = argInt->trueBits(); begend.first != begend.second; ++begend.first++)
      {
        auto bit_it = begend.first;
        const OrdinaryAtom& atom = argInt->getAtomToBit(bit_it);
        assert(atom.tuple.size() == 2);
        s << "arg(" << printToString<RawPrinter>(atom.tuple[1], reg) << ").\n";
      }
    }

    // attacks
    {
      PredicateMask& attMask = getPredicateMask(attRelId, reg);
      attMask.updateMask();
      InterpretationPtr attInt(new Interpretation(*query.interpretation));
      attInt->bit_and(*attMask.mask());
      for(auto begend = attInt->trueBits(); begend.first != begend.second; ++begend.first++)
      {
        auto bit_it = begend.first;
        const OrdinaryAtom& atom = attInt->getAtomToBit(bit_it);
        assert(atom.tuple.size() == 3);
        s << "att(" << printToString<RawPrinter>(atom.tuple[1], reg) << "," << printToString<RawPrinter>(atom.tuple[2], reg) << ").\n";
      }
    }

    // extension to check
    {
      PredicateMask& extMask = getPredicateMask(extRelId, reg);
      extMask.updateMask();
      InterpretationPtr extInt(new Interpretation(*query.interpretation));
      extInt->bit_and(*extMask.mask());
      for(auto begend = extInt->trueBits(); begend.first != begend.second; ++begend.first++)
      {
        auto bit_it = begend.first;
        const OrdinaryAtom& atom = extInt->getAtomToBit(bit_it);
        assert(atom.tuple.size() == 2);
        s << "ext(" << printToString<RawPrinter>(atom.tuple[1], reg) << ").\n";
      }
    }

    // add check
    s << "%% check if ext/1 is an extension\n"
         ":- arg(X), ext(X), out(X).\n"
         ":- arg(X), not ext(X), in(X).\n";
  }

  // build program
  InputProviderPtr input(new InputProvider);
  input->addStringInput(s.str(),"facts_from_predicate_input");
  input->addFileInput(semantics + ".encoding");

  #if 0
  // we use an extra registry for an external program
  ProgramCtx extctx;
  extctx.setupRegistry(RegistryPtr(new Registry));

  // parse
  ModuleHexParser parser;
  parser.parse(input, extctx);

  DBGLOG(DBG,"after parsing input: idb and edb are" << std::endl << std::endl <<
      printManyToString<RawPrinter>(extctx.idb,"\n",extctx.registry()) << std::endl <<
      *extctx.edb << std::endl);

  // check if there is one answer set, if yes return true, false otherwise
  {
    typedef ASPSolverManager::SoftwareConfiguration<ASPSolver::DLVSoftware> DLVConfiguration;
    DLVConfiguration dlv;
    OrdinaryASPProgram program(extctx.registry(), extctx.idb, extctx.edb, extctx.maxint);
    ASPSolverManager mgr;
    ASPSolverManager::ResultsPtr res = mgr.solve(dlv, program);
    AnswerSet::Ptr firstAnswerSet = res->getNextAnswerSet();
    if( firstAnswerSet != 0 )
    {
      LOG(DBG,"got answer set " << *firstAnswerSet->interpretation);
      // true
      answer.get().push_back(Tuple());
    }
    else
    {
      LOG(DBG,"got no answer set!");
      // false (-> mark as used)
      answer.use();
    }
  }
  #else
  ProgramCtx subctx = ctx;
  subctx.changeRegistry(RegistryPtr(new Registry));
  subctx.edb.reset(new Interpretation(subctx.registry()));

  subctx.inputProvider = input;
  input.reset();

  // parse into subctx, but do not call converters
  if( !subctx.parser )
  {
    subctx.parser.reset(new ModuleHexParser);
  }
  subctx.parser->parse(subctx.inputProvider, subctx);

  std::vector<InterpretationPtr> subas =
    ctx.evaluateSubprogram(subctx, false);
  if( !subas.empty() )
  {
    LOG(DBG,"got answer set " << *subas.front());
    // true
    answer.get().push_back(Tuple());
  }
  else
  {
    LOG(DBG,"got no answer set!");
    // false (-> mark as used)
    answer.use();
  }
  #endif
}