2451 lines
70 KiB
C++
2451 lines
70 KiB
C++
|
|
|
|
/*
|
|
----
|
|
This file is part of SECONDO.
|
|
|
|
Copyright (C) 2017, University in Hagen, Faculty of Mathematics and
|
|
Computer Science, Database Systems for New Applications.
|
|
|
|
SECONDO is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
SECONDO is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with SECONDO; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
----
|
|
|
|
*/
|
|
|
|
|
|
#include "Dijkstra.h"
|
|
|
|
#include "Algebras/Relation-C++/RelationAlgebra.h"
|
|
#include "StandardTypes.h"
|
|
#include "Attribute.h"
|
|
#include "Algebras/Standard-C++/LongInt.h"
|
|
#include "NestedList.h"
|
|
#include "QueryProcessor.h"
|
|
#include "Algebra.h"
|
|
#include "ListUtils.h"
|
|
#include "Stream.h"
|
|
#include "Symbols.h"
|
|
|
|
#include <string>
|
|
|
|
#include <map>
|
|
#include <vector>
|
|
#include <assert.h>
|
|
#include <limits>
|
|
#include <stack>
|
|
#include "AvlTree.h"
|
|
#include "mmheap.h"
|
|
|
|
|
|
extern NestedList* nl;
|
|
extern QueryProcessor* qp;
|
|
|
|
|
|
|
|
/*
|
|
1 General Dijkstra
|
|
|
|
This module implements a dijkstra algorithm without
|
|
knowlegde about underlying structures. Instead of use
|
|
of a fixed structure, e.g. an ordered relation, functions
|
|
are use to retrieve the successors of a node, or the
|
|
costs of an edge.
|
|
|
|
|
|
*/
|
|
|
|
|
|
namespace general_dijkstra{
|
|
|
|
|
|
|
|
|
|
/*
|
|
2 Type Map operator
|
|
|
|
*/
|
|
|
|
ListExpr GDTM(ListExpr args){
|
|
if(!nl->HasMinLength(args,1)){
|
|
return listutils::typeError("at least one argument expected");
|
|
}
|
|
|
|
ListExpr fun = nl->First(args);
|
|
if(!listutils::isMap<1>(fun)){
|
|
return listutils::typeError("first arg is not an unary function");
|
|
}
|
|
ListExpr stream = nl->Third(fun);
|
|
if(!Stream<Tuple>::checkType(stream)){
|
|
return listutils::typeError("function result is not a tuple stream");
|
|
}
|
|
return nl->Second(stream);
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
Type Mapping
|
|
|
|
The general dijkstra has the following inputs:
|
|
|
|
a function returning the successors of a node
|
|
as a stream of tuples together with an attribute
|
|
name giving the attribute of the target node
|
|
|
|
the attribute name of the target attribute
|
|
|
|
the initial node
|
|
|
|
the target node
|
|
|
|
a function computing the costs for an edge
|
|
|
|
the mode
|
|
|
|
a maximum depth, if the target node is not reached,
|
|
the result will be empty (optionally)
|
|
|
|
The disjktra produces a stream of tuples describing the
|
|
edges extended by a number.
|
|
|
|
*/
|
|
ListExpr gdijkstraTM(ListExpr args){
|
|
|
|
if(!nl->HasLength(args,7)){
|
|
return listutils::typeError("5 or 6 arguments expected");
|
|
}
|
|
ListExpr fun = nl->First(args);
|
|
if(!listutils::isMap<1>(fun)){
|
|
return listutils::typeError("first arg must be an unary function");
|
|
}
|
|
|
|
ListExpr nodeType = nl->Second(fun);
|
|
if(!CcInt::checkType(nodeType)
|
|
&& !LongInt::checkType(nodeType)){
|
|
return listutils::typeError("argument of the first function must "
|
|
"be of type int or longint");
|
|
}
|
|
|
|
ListExpr succStream = nl->Third(fun);
|
|
if(!Stream<Tuple>::checkType(succStream)){
|
|
return listutils::typeError("result of the first function must be "
|
|
"a tuple stream");
|
|
}
|
|
ListExpr edgeType = nl->Second(succStream);
|
|
|
|
if(!listutils::isSymbol(nl->Second(args))){
|
|
return listutils::typeError("Second arg is not an valid attribute name");
|
|
}
|
|
|
|
ListExpr attrList = nl->Second(edgeType);
|
|
ListExpr attrType;
|
|
std::string attrName = nl->SymbolValue(nl->Second(args));
|
|
int index = listutils::findAttribute(attrList, attrName, attrType);
|
|
if(!index){
|
|
return listutils::typeError("Attribute " + attrName
|
|
+ " not found in tuple");
|
|
}
|
|
if(!nl->Equal(nodeType, attrType)){
|
|
return listutils::typeError("function argument and type of target "
|
|
"node in tuple differ");
|
|
}
|
|
|
|
if(!nl->Equal(nodeType, nl->Third(args))){
|
|
return listutils::typeError("function argument and typr of start "
|
|
"node differ");
|
|
}
|
|
if(!nl->Equal(nodeType, nl->Fourth(args))){
|
|
return listutils::typeError("function argument and type of target "
|
|
"node differ");
|
|
}
|
|
|
|
ListExpr costFun = nl->Fifth(args);
|
|
if(!listutils::isMap<1>(costFun)){
|
|
return listutils::typeError("5th argument (cost function) is not "
|
|
"a unary function");
|
|
}
|
|
|
|
if(!nl->Equal(nl->Second(costFun), edgeType)){
|
|
return listutils::typeError("cost function argument differs to edge type "
|
|
"defined by the successor fucntion");
|
|
}
|
|
|
|
if(!CcReal::checkType(nl->Third(costFun))){
|
|
return listutils::typeError("cost function does not returns a double");
|
|
}
|
|
|
|
if(!CcInt::checkType(nl->Sixth(args))){
|
|
return listutils::typeError("6th argument (mode) is not an int");
|
|
}
|
|
|
|
ListExpr extendTuple = nl->ThreeElemList(
|
|
nl->TwoElemList(
|
|
nl->SymbolAtom("EdgeNoSP"),
|
|
listutils::basicSymbol<CcInt>()),
|
|
nl->TwoElemList(
|
|
nl->SymbolAtom("CostsSP"),
|
|
listutils::basicSymbol<CcReal>()),
|
|
nl->TwoElemList(
|
|
nl->SymbolAtom("PredSP"),
|
|
nodeType
|
|
)
|
|
);
|
|
|
|
ListExpr resAttrList = listutils::concat(attrList, extendTuple);
|
|
if(!listutils::isAttrList(resAttrList)){
|
|
return listutils::typeError("EdgeType contains already an "
|
|
"EdgeNoSP attribute");
|
|
}
|
|
|
|
ListExpr resType = nl->TwoElemList(
|
|
listutils::basicSymbol<Stream<Tuple> >(),
|
|
nl->TwoElemList(
|
|
listutils::basicSymbol<Tuple>(),
|
|
resAttrList));
|
|
|
|
if(!CcInt::checkType(nl->Sixth(nl->Rest(args)))){
|
|
return listutils::typeError("7th arg (maxDepth) must be of type int");
|
|
}
|
|
|
|
|
|
return nl->ThreeElemList(
|
|
nl->SymbolAtom(Symbols::APPEND()),
|
|
nl->OneElemList( nl->IntAtom(index -1)),
|
|
resType);
|
|
|
|
}
|
|
|
|
template<typename T>
|
|
class treeEntry{
|
|
public:
|
|
|
|
treeEntry(): pred(0),costs(0),depth(0),edge(0){}
|
|
|
|
treeEntry(T _pred, double _costs,
|
|
uint32_t _depth, Tuple* _edge):
|
|
pred(_pred), costs(_costs), depth(_depth),
|
|
edge(_edge){
|
|
assert(edge);
|
|
}
|
|
|
|
treeEntry(const treeEntry<T>& e):
|
|
pred(e.pred), costs(e.costs), depth(e.depth),
|
|
edge(e.edge){}
|
|
|
|
|
|
bool operator<(const treeEntry<T>& e)const{
|
|
if(costs < e.costs) return true;
|
|
if(costs > e.costs) return false;
|
|
if(pred < e.pred) return true;
|
|
if(pred > e.pred) return false;
|
|
if(depth < e.depth) return true;
|
|
if(depth > e.depth) return false;
|
|
return false;
|
|
}
|
|
|
|
bool operator>(const treeEntry<T>& e)const{
|
|
if(costs > e.costs) return true;
|
|
if(costs < e.costs) return false;
|
|
if(pred > e.pred) return true;
|
|
if(pred < e.pred) return false;
|
|
if(depth > e.depth) return true;
|
|
if(depth < e.depth) return false;
|
|
return false;
|
|
}
|
|
|
|
T pred; // predecessor node
|
|
double costs; // current costs
|
|
uint32_t depth; // current depth
|
|
Tuple* edge; // edge from pred to target
|
|
};
|
|
|
|
|
|
template<class T>
|
|
class queueEntry{
|
|
|
|
public:
|
|
|
|
queueEntry(T* _node, double _costs, int _depth):
|
|
node(_node) , costs(_costs), depth(_depth)
|
|
{
|
|
nodeValue = node->GetValue();
|
|
|
|
}
|
|
|
|
bool operator<(const queueEntry<T>& other) const{
|
|
if(costs > other.costs) return true;
|
|
return false;
|
|
}
|
|
|
|
std::ostream& print(std::ostream& out) const{
|
|
out << "node : " << node->GetValue() << ", costs : "
|
|
<< costs << ", depth : " << depth;
|
|
return out;
|
|
}
|
|
|
|
T* node;
|
|
typename T::ctype nodeValue;
|
|
double costs;
|
|
uint32_t depth;
|
|
|
|
};
|
|
|
|
|
|
class queueentryComp{
|
|
public:
|
|
template<class T>
|
|
bool operator()(const queueEntry<T>& f, const queueEntry<T>& s) const {
|
|
return f.costs < s.costs;
|
|
}
|
|
};
|
|
|
|
|
|
template<class T>
|
|
std::ostream& operator<<(std::ostream& o, const queueEntry<T>& e){
|
|
return e.print(o);
|
|
}
|
|
|
|
template<class Key, class Value>
|
|
class avlmapentry{
|
|
public:
|
|
avlmapentry(Key& _key, Value& _value):
|
|
key(_key), value(_value){}
|
|
|
|
Key key;
|
|
Value value;
|
|
};
|
|
|
|
class avlMapComp{
|
|
public:
|
|
template<class Key, class Value>
|
|
static bool smaller(const avlmapentry<Key,Value>& o1,
|
|
const avlmapentry<Key,Value>& o2){
|
|
return o1.key < o2.key;
|
|
}
|
|
template<class Key, class Value>
|
|
static bool equal(const avlmapentry<Key,Value>& o1,
|
|
const avlmapentry<Key,Value>& o2){
|
|
return o1.key == o2.key;
|
|
}
|
|
template<class Key, class Value>
|
|
static bool greater(const avlmapentry<Key,Value>& o1,
|
|
const avlmapentry<Key,Value>& o2){
|
|
return o1.key > o2.key;
|
|
}
|
|
};
|
|
|
|
|
|
|
|
template<class T>
|
|
class gdijkstraInfo{
|
|
|
|
public:
|
|
|
|
gdijkstraInfo(Supplier _succfun,
|
|
T* _initialNode,
|
|
T* _targetNode,
|
|
Supplier _costFun,
|
|
int _mode,
|
|
int _maxDepth,
|
|
int _targetPos,
|
|
ListExpr _tt):
|
|
succFun(_succfun),
|
|
initialNode(_initialNode->GetValue()),
|
|
targetNode(_targetNode->GetValue()),
|
|
costFun(_costFun),
|
|
mode(_mode),
|
|
maxDepth(_maxDepth),
|
|
targetPos(_targetPos){
|
|
tt = new TupleType(_tt);
|
|
succFunArg = qp->Argument(succFun);
|
|
costFunArg = qp->Argument(costFun);
|
|
found = initialNode==targetNode;
|
|
queueEntry<T> qe(_initialNode, 0,0);
|
|
front.push(qe);
|
|
if(mode==0 || mode==1){
|
|
dijkstra();
|
|
}
|
|
}
|
|
|
|
|
|
~gdijkstraInfo(){
|
|
tt->DeleteIfAllowed();
|
|
// remove edges in tree
|
|
tree.destroy(&kill);
|
|
while(!yellowEdges.empty()){
|
|
yellowEdges.back()->DeleteIfAllowed();
|
|
yellowEdges.pop_back();
|
|
}
|
|
}
|
|
|
|
|
|
Tuple* next(){
|
|
switch(mode){
|
|
case 0: return next0(); // shortest path only
|
|
case 1: return next1(); // edges to the front
|
|
case 2: return next2(); // all visited edges
|
|
case 3: return next3(); // shortest path tree
|
|
}
|
|
return 0; // not implemented mode
|
|
}
|
|
|
|
|
|
double getCosts(){
|
|
assert(mode==4);
|
|
Tuple* tup;
|
|
while(!found && ((tup=next3())!=0)){
|
|
tup->DeleteIfAllowed();
|
|
}
|
|
if(tup){
|
|
tup->DeleteIfAllowed();
|
|
}
|
|
if(!found) {
|
|
return std::numeric_limits<double>::max();
|
|
}
|
|
treeEntry<ct> entry = tree[targetNode];
|
|
return entry.costs();
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private:
|
|
Supplier succFun;
|
|
typename T::ctype initialNode;
|
|
typename T::ctype targetNode;
|
|
Supplier costFun;
|
|
int mode;
|
|
int maxDepth;
|
|
int targetPos;
|
|
TupleType* tt;
|
|
ArgVectorPointer succFunArg;
|
|
ArgVectorPointer costFunArg;
|
|
|
|
|
|
bool found; // used in mode 0 and 1
|
|
|
|
typedef typename T::ctype ct;
|
|
|
|
ct currentTarget; // for mode 1 only
|
|
|
|
std::vector<Tuple*> yellowEdges; // for mode 2 only
|
|
|
|
|
|
// representation of the current tree
|
|
typedef avlmapentry<ct, treeEntry<ct> > avlentry;
|
|
typedef avltree::AVLTree<avlentry,avlMapComp > tree_t;
|
|
|
|
tree_t tree;
|
|
|
|
|
|
typedef mmheap::mmheap<queueEntry<T>, queueentryComp> prio_queue_t;
|
|
//typedef std::priority_queue<queueEntry<T> > prio_queue_t;
|
|
prio_queue_t front;
|
|
avltree::AVLTree<ct> processedNodes;
|
|
|
|
|
|
static void kill(avlentry& e){
|
|
e.value.edge->DeleteIfAllowed();
|
|
e.value.edge = 0;
|
|
}
|
|
|
|
|
|
|
|
inline Tuple* createResultTuple( treeEntry<ct>& entry){
|
|
return createResultTuple(entry.edge, entry.depth,
|
|
entry.costs,entry.pred);
|
|
}
|
|
|
|
Tuple* createResultTuple(Tuple* oedge, int depth, double costs, ct pred){
|
|
// create result tuple from edge and some counter
|
|
Tuple* res = new Tuple(tt);
|
|
for(int i=0;i<oedge->GetNoAttributes(); i++){
|
|
res->CopyAttribute(i,oedge,i);
|
|
}
|
|
res->PutAttribute(oedge->GetNoAttributes(),
|
|
new CcInt(true,depth));
|
|
res->PutAttribute(oedge->GetNoAttributes()+1,
|
|
new CcReal(true,costs));
|
|
|
|
treeEntry<ct> te;
|
|
avlentry ae(pred,te);
|
|
avlentry* old = tree.getMember(ae);
|
|
T* ppred;
|
|
if(!old){
|
|
ppred = new T(false);
|
|
} else {
|
|
ppred = new T(true, old->value.pred);
|
|
}
|
|
res->PutAttribute(oedge->GetNoAttributes()+2,ppred);
|
|
currentTarget = pred;
|
|
oedge->DeleteIfAllowed();
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
~processNode~
|
|
|
|
This node will never be changed in the future.
|
|
All successor of this node will be processed.
|
|
|
|
|
|
*/
|
|
|
|
void processNode(T* node,ct nvalue, double costs, uint32_t depth){
|
|
if(processedNodes.member(nvalue)){
|
|
// node already processed. this case may occur because
|
|
// updates values are not remove from the priority
|
|
// queue
|
|
return;
|
|
}
|
|
processedNodes.insert(node->GetValue());
|
|
if((mode!=3) && // in mode 3, the target node is ignored
|
|
(node->GetValue() == targetNode)){
|
|
found = true;
|
|
return;
|
|
}
|
|
|
|
if((maxDepth>0) && (depth>=(uint32_t)maxDepth)){
|
|
// early stop because of path length restriction
|
|
return;
|
|
}
|
|
|
|
(*succFunArg)[0] = node;
|
|
qp->Open(succFun);
|
|
Word value;
|
|
qp->Request(succFun,value);
|
|
while(qp->Received(succFun)){
|
|
processEdge(node, costs, (Tuple*) value.addr,
|
|
depth);
|
|
qp->Request(succFun,value);
|
|
}
|
|
qp->Close(succFun);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
~processEdge~
|
|
|
|
Processes a successor of a node. If the edge is invalid,
|
|
this edge is ignored completely, except in mode 2.
|
|
|
|
*/
|
|
void processEdge(T* node, double nc,
|
|
Tuple* edge, uint32_t depth){
|
|
|
|
T* target = (T*) edge->GetAttribute(targetPos);
|
|
if(!target->IsDefined()){
|
|
if(mode!=2){
|
|
edge->DeleteIfAllowed();
|
|
} else {
|
|
yellowEdges.push_back(createResultTuple(edge,-1*(depth+1),
|
|
-1,node->GetValue()));
|
|
}
|
|
return;
|
|
}
|
|
|
|
bool ok;
|
|
double costs = getCosts(edge, ok);
|
|
if(!ok){
|
|
if(mode!=2){
|
|
edge->DeleteIfAllowed();
|
|
} else {
|
|
yellowEdges.push_back(createResultTuple(edge,-1*(depth+1),
|
|
-1,node->GetValue()));
|
|
}
|
|
return;
|
|
}
|
|
|
|
if(costs<0){
|
|
if(mode!=2){
|
|
edge->DeleteIfAllowed();
|
|
} else {
|
|
yellowEdges.push_back(createResultTuple(edge,-1*(depth+1),
|
|
-1,node->GetValue()));
|
|
}
|
|
return;
|
|
}
|
|
|
|
typename T::ctype t = target->GetValue();
|
|
double wc = nc + costs;
|
|
|
|
if(!processedNodes.member(t)){
|
|
treeEntry<ct> te(node->GetValue(), wc, depth+1, edge);
|
|
avlentry ae(t,te);
|
|
bool found;
|
|
avlentry* oldentry = tree.insert3(ae,found);
|
|
if(!found){
|
|
// t found the first time
|
|
front.push( queueEntry<T>(target,wc,depth+1));
|
|
} else {
|
|
// check for shorter path to t
|
|
if(wc < oldentry->value.costs){
|
|
std::swap(te,oldentry->value);
|
|
if(mode!=2){
|
|
te.edge->DeleteIfAllowed();
|
|
} else {
|
|
yellowEdges.push_back(createResultTuple(te.edge,
|
|
-1*(depth+1),
|
|
te.costs,node->GetValue()));
|
|
}
|
|
front.push( queueEntry<T>(target,wc,depth+1));
|
|
} else {
|
|
if(mode!=2){
|
|
edge->DeleteIfAllowed();
|
|
} else {
|
|
yellowEdges.push_back(createResultTuple(edge,-1*(depth+1),
|
|
wc,node->GetValue()));
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if(mode!=2){
|
|
edge->DeleteIfAllowed();
|
|
} else {
|
|
yellowEdges.push_back(createResultTuple(edge,-1*(depth+1),
|
|
wc,node->GetValue()));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
double getCosts(Tuple* t, bool& correct){
|
|
(*costFunArg)[0].addr = t;
|
|
Word value;
|
|
qp->Request(costFun, value);
|
|
CcReal* g = (CcReal*) value.addr;
|
|
correct = g->IsDefined();
|
|
if(!correct){
|
|
return -1;
|
|
}
|
|
return g->GetValue();
|
|
}
|
|
|
|
void dijkstra(){
|
|
while(!found && !front.empty()){
|
|
queueEntry<T> n = front.top();
|
|
front.pop();
|
|
processNode(n.node,n.nodeValue, n.costs,n.depth);
|
|
}
|
|
currentTarget = found?targetNode:initialNode;
|
|
|
|
}
|
|
|
|
Tuple* next0(){ // path version
|
|
if(!found){
|
|
return 0;
|
|
}
|
|
if(currentTarget == initialNode){
|
|
return 0;
|
|
}
|
|
treeEntry<ct> entry;
|
|
avlentry ae(currentTarget,entry);
|
|
tree.remove(ae);
|
|
return createResultTuple(ae.value);
|
|
}
|
|
|
|
|
|
Tuple* next3(){ // tree version
|
|
while(!front.empty() && !found){
|
|
queueEntry<T> n = front.top();
|
|
front.pop();
|
|
if(!processedNodes.member(n.nodeValue)){
|
|
processNode(n.node, n.nodeValue,n.costs,n.depth);
|
|
if(n.nodeValue!=initialNode){ // the initial node has no pred
|
|
treeEntry<ct> entry;
|
|
avlentry ae(n.nodeValue,entry);
|
|
tree.remove(ae);
|
|
return createResultTuple(ae.value);
|
|
}
|
|
}
|
|
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
Tuple* next2(){ // all edges version
|
|
if(!yellowEdges.empty()){
|
|
Tuple* res = yellowEdges.back();
|
|
yellowEdges.pop_back();
|
|
return res;
|
|
}
|
|
if(found) return 0;
|
|
return next3();
|
|
}
|
|
|
|
Tuple* next1(){ // front version
|
|
while(!front.empty()){
|
|
queueEntry<T> n = front.top();
|
|
front.pop();
|
|
if( !processedNodes.member(n.nodeValue))
|
|
{
|
|
processedNodes.insert(n.nodeValue);
|
|
treeEntry<ct> entry;
|
|
avlentry ae(n.nodeValue, entry);
|
|
tree.remove(ae);
|
|
return createResultTuple(ae.value);
|
|
|
|
}
|
|
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
};
|
|
|
|
template<class T>
|
|
int gdijkstraVMT(Word* args, Word& result, int message,
|
|
Word& local, Supplier s) {
|
|
|
|
gdijkstraInfo<T>* li = (gdijkstraInfo<T>*) local.addr;
|
|
|
|
switch(message){
|
|
case OPEN:{
|
|
if(li){
|
|
delete li;
|
|
local.addr = 0;
|
|
}
|
|
T* source = (T*) args[2].addr;
|
|
if(!source->IsDefined()){
|
|
return 0;
|
|
}
|
|
T* target = (T*) args[3].addr;
|
|
if(!target->IsDefined()){
|
|
return 0;
|
|
}
|
|
CcInt* mode = (CcInt*) args[5].addr;
|
|
if(!mode->IsDefined()){
|
|
return 0;
|
|
}
|
|
int mode1 = mode->GetValue();
|
|
|
|
int depth = -1;
|
|
CcInt* d = (CcInt*) args[6].addr;
|
|
if(d->IsDefined()){
|
|
depth = d->GetValue();
|
|
}
|
|
int attrPos = ((CcInt*) args[7].addr)->GetValue();
|
|
local.addr = new gdijkstraInfo<T>(
|
|
args[0].addr,
|
|
source, target, args[4].addr,
|
|
mode1, depth, attrPos,
|
|
nl->Second(GetTupleResultType(s)));
|
|
return 0;
|
|
}
|
|
|
|
case REQUEST:
|
|
result.addr = li?li->next():0;
|
|
return result.addr?YIELD:CANCEL;
|
|
case CLOSE:
|
|
if(li){
|
|
delete li;
|
|
local.addr = 0;
|
|
}
|
|
}
|
|
return-1;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template int gdijkstraVMT<CcInt>(Word* args, Word& result, int message,
|
|
Word& local, Supplier s);
|
|
|
|
template int gdijkstraVMT<LongInt>(Word* args, Word& result, int message,
|
|
Word& local, Supplier s);
|
|
|
|
|
|
|
|
/*
|
|
|
|
2 minPathCosts1 Operator.
|
|
|
|
Computes the costs of a path from one node to another one using the
|
|
same graph representation (as a successor function) as the general
|
|
dijkstra. In version 1, the costs are already stored as a attribute
|
|
in each edge tuple, in version 2, a function is used.
|
|
|
|
2.1 Type Mapping
|
|
|
|
Arg 1: successor function
|
|
|
|
Arg 2: attribute name of the target attribute
|
|
|
|
Arg 3: the initial node
|
|
|
|
Arg 4: the target node
|
|
|
|
Arg 5: attribute containing or function computing the costs of an edge
|
|
|
|
Arg 6: maximim depth
|
|
|
|
If no path is found, the returned costs correspond to the maximum double
|
|
value.
|
|
|
|
*/
|
|
|
|
template<bool useFun>
|
|
ListExpr minPathCostsTM(ListExpr args){
|
|
if(!nl->HasLength(args,6)){
|
|
return listutils::typeError("6 args expected");
|
|
}
|
|
// arg 1 must be a function from int, longint -> stream(tuple)
|
|
ListExpr fun = nl->First(args);
|
|
if(!listutils::isMap<1>(fun)){
|
|
return listutils::typeError("first arg must be an unary function");
|
|
}
|
|
// check function argument
|
|
ListExpr nodeType = nl->Second(fun);
|
|
if(!CcInt::checkType(nodeType)
|
|
&& !LongInt::checkType(nodeType)){
|
|
return listutils::typeError("argument of the first function must "
|
|
"be of type int or longint");
|
|
}
|
|
// check function result
|
|
ListExpr succStream = nl->Third(fun);
|
|
if(!Stream<Tuple>::checkType(succStream)){
|
|
return listutils::typeError("result of the first function must be "
|
|
"a tuple stream");
|
|
}
|
|
ListExpr edgeType = nl->Second(succStream);
|
|
|
|
// second argument must be an attribute name in the function result stream
|
|
if(!listutils::isSymbol(nl->Second(args))){
|
|
return listutils::typeError("Second arg is not an valid attribute name");
|
|
}
|
|
|
|
ListExpr attrList = nl->Second(edgeType);
|
|
ListExpr attrType;
|
|
std::string attrName = nl->SymbolValue(nl->Second(args));
|
|
int index = listutils::findAttribute(attrList, attrName, attrType);
|
|
if(!index){
|
|
return listutils::typeError("Attribute " + attrName
|
|
+ " not found in tuple");
|
|
}
|
|
if(!nl->Equal(nodeType, attrType)){
|
|
return listutils::typeError("function argument and type of target "
|
|
"node in tuple differ");
|
|
}
|
|
|
|
// the third and fourth argument must have the same type as the
|
|
// function argument
|
|
|
|
if(!nl->Equal(nodeType, nl->Third(args))){
|
|
return listutils::typeError("function argument and typr of start "
|
|
"node differ");
|
|
}
|
|
if(!nl->Equal(nodeType, nl->Fourth(args))){
|
|
return listutils::typeError("function argument and type of target "
|
|
"node differ");
|
|
}
|
|
|
|
// the fifth arguments depends on the template argument
|
|
int index2 = -1;
|
|
|
|
if(useFun){
|
|
ListExpr costFun = nl->Fifth(args);
|
|
if(!listutils::isMap<1>(costFun)){
|
|
return listutils::typeError("5th argument (cost function) is not "
|
|
"a unary function");
|
|
}
|
|
// check arg
|
|
if(!nl->Equal(nl->Second(costFun), edgeType)){
|
|
return listutils::typeError("cost function argument differs to "
|
|
"edge type defined by the successor fucntion");
|
|
}
|
|
// check result
|
|
if(!CcReal::checkType(nl->Third(costFun))){
|
|
return listutils::typeError("cost function does not returns a double");
|
|
}
|
|
} else {
|
|
// in this case the 5th arg must be an edge attribute name of type double
|
|
if(!listutils::isSymbol(nl->Fifth(args))){
|
|
return listutils::typeError("5th arg is not a valid attribute name");
|
|
}
|
|
std::string costAttr = nl->SymbolValue(nl->Fifth(args));
|
|
index2 = listutils::findAttribute(attrList, costAttr, attrType);
|
|
if(!index2){
|
|
return listutils::typeError("cost attribute " + costAttr
|
|
+ " not part of the edge tuple");
|
|
}
|
|
if(!CcReal::checkType(attrType)){
|
|
return listutils::typeError("cost attribute " + costAttr
|
|
+ " not of type real");
|
|
|
|
}
|
|
}
|
|
|
|
|
|
if(!CcInt::checkType(nl->Sixth(args))){
|
|
return listutils::typeError("6th argument (mode) is not an int");
|
|
}
|
|
|
|
return nl->ThreeElemList(
|
|
nl->SymbolAtom(Symbols::APPEND()),
|
|
nl->TwoElemList( nl->IntAtom(index -1), nl->IntAtom(index2-1)),
|
|
listutils::basicSymbol<CcReal>());
|
|
|
|
}
|
|
|
|
|
|
template ListExpr minPathCostsTM<true>(ListExpr args);
|
|
template ListExpr minPathCostsTM<false>(ListExpr args);
|
|
|
|
|
|
template<class T>
|
|
class minPathCostsInfo{
|
|
public:
|
|
minPathCostsInfo(Word _succFun,
|
|
T* _sourceNode,
|
|
T* _targetNode,
|
|
int _maxHops,
|
|
int _targetPos,
|
|
int _costPos):
|
|
succFun(_succFun.addr),
|
|
sourceNode(_sourceNode),
|
|
targetNode(_targetNode->GetValue()),
|
|
maxHops(_maxHops),
|
|
targetPos(_targetPos),
|
|
costPos(_costPos)
|
|
{
|
|
queueEntry<T> qe((T*) _sourceNode->Copy(), 0,0);
|
|
front.push(qe);
|
|
found =sourceNode->GetValue() == targetNode;
|
|
targetCosts = std::numeric_limits<double>::max();
|
|
succFunArg = qp->Argument(succFun);
|
|
}
|
|
|
|
|
|
minPathCostsInfo(Word _succFun,
|
|
T* _sourceNode,
|
|
T* _targetNode,
|
|
int _maxHops,
|
|
int _targetPos,
|
|
Word _costFun):
|
|
succFun(_succFun.addr),
|
|
sourceNode(_sourceNode),
|
|
targetNode(_targetNode->GetValue()),
|
|
maxHops(_maxHops),
|
|
targetPos(_targetPos),
|
|
costPos(-1),
|
|
costFun(_costFun.addr)
|
|
{
|
|
queueEntry<T> qe((T*)_sourceNode->Copy(), 0,0);
|
|
front.push(qe);
|
|
found =sourceNode->GetValue() == targetNode;
|
|
targetCosts = std::numeric_limits<double>::max();
|
|
succFunArg = qp->Argument(succFun);
|
|
costFunArg = qp->Argument(costFun);
|
|
}
|
|
|
|
|
|
~minPathCostsInfo(){
|
|
while(!front.empty()){
|
|
queueEntry<T> t = front.top();
|
|
front.pop();
|
|
t.node->DeleteIfAllowed();
|
|
}
|
|
}
|
|
|
|
|
|
double getCosts(){
|
|
while(!found && !front.empty()){
|
|
queueEntry<T> e = front.top();
|
|
front.pop();
|
|
processNode(e);
|
|
e.node->DeleteIfAllowed();
|
|
}
|
|
|
|
return targetCosts;
|
|
}
|
|
|
|
private:
|
|
typedef typename T::ctype ct;
|
|
Supplier succFun;
|
|
T* sourceNode;
|
|
ct targetNode;
|
|
int maxHops;
|
|
int targetPos;
|
|
int costPos;
|
|
|
|
Supplier costFun;
|
|
ArgVectorPointer costFunArg;
|
|
|
|
|
|
typedef mmheap::mmheap<queueEntry<T>, queueentryComp> prio_queue_t;
|
|
// typedef std::priority_queue<queueEntry<T> > prio_queue_t;
|
|
prio_queue_t front;
|
|
avltree::AVLTree<ct> processedNodes;
|
|
bool found;
|
|
double targetCosts;
|
|
ArgVectorPointer succFunArg;
|
|
std::map<ct,double> frontCosts;
|
|
|
|
|
|
void processNode(queueEntry<T>& e){
|
|
|
|
if(processedNodes.member(e.nodeValue)){
|
|
return;
|
|
}
|
|
if(e.nodeValue==targetNode){
|
|
found = true;
|
|
targetCosts = e.costs;
|
|
return;
|
|
}
|
|
processedNodes.insert(e.nodeValue);
|
|
frontCosts.erase(e.nodeValue);
|
|
|
|
if(maxHops>0 && e.depth==(uint32_t)maxHops){
|
|
return;
|
|
}
|
|
// cancel computation not possible, process successors
|
|
(*succFunArg)[0] = e.node;
|
|
qp->Open(succFun);
|
|
Word value;
|
|
qp->Request(succFun,value);
|
|
while(qp->Received(succFun)){
|
|
processEdge(e, (Tuple*) value.addr);
|
|
qp->Request(succFun,value);
|
|
}
|
|
qp->Close(succFun);
|
|
}
|
|
|
|
|
|
void processEdge(queueEntry<T>& e, Tuple* tup){
|
|
double costs = getCosts(tup);
|
|
if(costs<0){
|
|
tup->DeleteIfAllowed();
|
|
return;
|
|
}
|
|
T* target = (T*) tup->GetAttribute(targetPos);
|
|
if(!target->IsDefined()){
|
|
tup->DeleteIfAllowed();
|
|
return;
|
|
}
|
|
ct targetVal = target->GetValue();
|
|
if(processedNodes.member(targetVal)){
|
|
tup->DeleteIfAllowed();
|
|
return;
|
|
}
|
|
target = (T*) target->Copy();
|
|
tup->DeleteIfAllowed();
|
|
double nc = e.costs + costs;
|
|
typename std::map<ct,double>::iterator it
|
|
= frontCosts.find(targetVal);
|
|
|
|
if(it==frontCosts.end()){ // node found the first time
|
|
// otherwise the node is either processed
|
|
// or in front
|
|
frontCosts[targetVal] = nc;
|
|
front.push(queueEntry<T>(target, nc, e.depth+1));
|
|
} else {
|
|
if(it->second<= nc){
|
|
target->DeleteIfAllowed();
|
|
} else {
|
|
it->second = nc; // update costs
|
|
front.push(queueEntry<T>(target, nc, e.depth+1));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
double getCosts(Tuple* tup){
|
|
if(costPos >=0){
|
|
CcReal* c = (CcReal*) tup->GetAttribute(costPos);
|
|
if(!c->IsDefined()){
|
|
return -1;
|
|
}
|
|
return c->GetValue();
|
|
} else {
|
|
(*costFunArg)[0].addr = tup;
|
|
Word value;
|
|
qp->Request(costFun, value);
|
|
CcReal* g = (CcReal*) value.addr;
|
|
if(!g->IsDefined()){
|
|
return -1;
|
|
} else {
|
|
return g->GetValue();
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|
|
template<class T>
|
|
int minPathCost1VMT(Word* args, Word& result, int message,
|
|
Word& local, Supplier s) {
|
|
|
|
result = qp->ResultStorage(s);
|
|
CcReal* res = (CcReal*) result.addr;
|
|
T* source = (T*) args[2].addr;
|
|
T* target = (T*) args[3].addr;
|
|
CcInt* mH = (CcInt*) args[5].addr;
|
|
|
|
if(!source->IsDefined() || !target->IsDefined() || !mH->IsDefined()){
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
|
|
int targetPos = ((CcInt*)args[6].addr)->GetValue();
|
|
int costPos = ((CcInt*)args[7].addr)->GetValue();
|
|
|
|
|
|
|
|
minPathCostsInfo<T> info(args[0],source,target,mH->GetValue(),
|
|
targetPos,costPos);
|
|
res->Set(true,info.getCosts());
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
template<class T>
|
|
int minPathCost2VMT(Word* args, Word& result, int message,
|
|
Word& local, Supplier s) {
|
|
|
|
result = qp->ResultStorage(s);
|
|
CcReal* res = (CcReal*) result.addr;
|
|
T* source = (T*) args[2].addr;
|
|
T* target = (T*) args[3].addr;
|
|
CcInt* mH = (CcInt*) args[5].addr;
|
|
|
|
if(!source->IsDefined() || !target->IsDefined() || !mH->IsDefined()){
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
|
|
int targetPos = ((CcInt*)args[6].addr)->GetValue();
|
|
|
|
|
|
|
|
minPathCostsInfo<T> info(args[0],source,target,mH->GetValue(),
|
|
targetPos,args[4]);
|
|
res->Set(true,info.getCosts());
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
template int minPathCost2VMT<CcInt>(Word* args, Word& result, int message,
|
|
Word& local, Supplier s);
|
|
|
|
template int minPathCost2VMT<LongInt>(Word* args, Word& result, int message,
|
|
Word& local, Supplier s);
|
|
|
|
|
|
|
|
template int minPathCost1VMT<CcInt>(Word* args, Word& result, int message,
|
|
Word& local, Supplier s);
|
|
|
|
template int minPathCost1VMT<LongInt>(Word* args, Word& result, int message,
|
|
Word& local, Supplier s);
|
|
|
|
|
|
/*
|
|
3 mtMinPathCosts
|
|
|
|
This operator works similar to the minPathcosts but computes the
|
|
minimum path costs for a set of targets.
|
|
|
|
*/
|
|
template<bool costsAsFun>
|
|
ListExpr mtMinPathCostsTM(ListExpr args){
|
|
if(!nl->HasLength(args,7) &&!nl->HasLength(args,8)){
|
|
return listutils::typeError("7 or 8 arguments expected");
|
|
}
|
|
/*
|
|
1 : successor function
|
|
2 : stream of targets (tuples)
|
|
3 : name of the target in successor function
|
|
4 : source of the search
|
|
5 : name of the target in the target stream
|
|
6 : name of the cost attribute or cost function (depends on template)
|
|
7 : maximum number of hops
|
|
8 : maximum costs (optional)
|
|
*/
|
|
// allowed node types: int, longint
|
|
ListExpr source = nl->Fourth(args);
|
|
if(!CcInt::checkType(source) && !LongInt::checkType(source)){
|
|
return listutils::typeError("invalid node type, supported are "
|
|
"int and longint");
|
|
}
|
|
ListExpr succFun = nl->First(args);
|
|
if(!listutils::isMap<1>(succFun)){
|
|
return listutils::typeError("first arg is not a map");
|
|
}
|
|
if(!nl->Equal(nl->Second(succFun), source)){
|
|
return listutils::typeError("type of source node and type of the "
|
|
"argument of the successor function differ");
|
|
|
|
}
|
|
ListExpr succstream = nl->Third(succFun);
|
|
if(!Stream<Tuple>::checkType(succstream)){
|
|
return listutils::typeError("successor function does not produce a "
|
|
"tuple stream");
|
|
}
|
|
ListExpr hops = nl->Sixth(nl->Rest(args));
|
|
if(!CcInt::checkType(hops)){
|
|
return listutils::typeError("7th argument not of type int");
|
|
}
|
|
if(nl->HasLength(args,8)
|
|
&& !CcReal::checkType(nl->Sixth(nl->Rest(nl->Rest(args))))){
|
|
return listutils::typeError("8th argument not of type real");
|
|
}
|
|
|
|
|
|
ListExpr succTargetName = nl->Third(args);
|
|
if(nl->AtomType(succTargetName)!=SymbolType){
|
|
return listutils::typeError("3rd argument is not a valid attribute name");
|
|
}
|
|
std::string succTarget = nl->SymbolValue(succTargetName);
|
|
ListExpr attrType;
|
|
ListExpr succAttrList = nl->Second(nl->Second(succstream));
|
|
int succIndex = listutils::findAttribute(succAttrList,succTarget,attrType);
|
|
if(!succIndex){
|
|
return listutils::typeError("attribute " + succTarget + " not found in "
|
|
"successor function result");
|
|
}
|
|
if(!nl->Equal(attrType,source)){
|
|
return listutils::typeError("type of source node and attribute " +
|
|
succTarget + " in successor function result"
|
|
" differ");
|
|
}
|
|
ListExpr targetSetName = nl->Fifth(args);
|
|
if(nl->AtomType(targetSetName)!=SymbolType){
|
|
return listutils::typeError("5th arg is not a valid attribute name");
|
|
}
|
|
std::string targetSet = nl->SymbolValue(targetSetName);
|
|
ListExpr targets = nl->Second(args);
|
|
if(!Stream<Tuple>::checkType(targets)){
|
|
return listutils::typeError("second arg must be a tuple stream");
|
|
}
|
|
ListExpr targetAttrList = nl->Second(nl->Second(targets));
|
|
int targetIndex = listutils::findAttribute(targetAttrList, targetSet,
|
|
attrType);
|
|
if(!targetIndex){
|
|
return listutils::typeError("attribute " + targetSet + " not found "
|
|
"in target stream");
|
|
}
|
|
if(!nl->Equal(attrType, source)){
|
|
return listutils::typeError("type of " + targetSet + " in target stream"
|
|
" differs to the type of the source node");
|
|
}
|
|
|
|
if(listutils::findAttribute(targetAttrList,"MinPC",attrType)){
|
|
return listutils::typeError("Attribute name MinPC already present in "
|
|
"target tuple");
|
|
}
|
|
|
|
ListExpr appAttr = nl->OneElemList( nl->TwoElemList(
|
|
nl->SymbolAtom("MinPC"),
|
|
listutils::basicSymbol<CcReal>()));
|
|
|
|
|
|
ListExpr resAttrList = listutils::concat(targetAttrList,appAttr);
|
|
|
|
ListExpr res = nl->TwoElemList(
|
|
listutils::basicSymbol<Stream<Tuple> >(),
|
|
nl->TwoElemList(
|
|
listutils::basicSymbol<Tuple>(),
|
|
resAttrList));
|
|
|
|
|
|
|
|
ListExpr costsArg = nl->Sixth(args);
|
|
if(!costsAsFun){
|
|
if(nl->AtomType(costsArg) != SymbolType){
|
|
return listutils::typeError("6th arg is not a valid attribute name");
|
|
}
|
|
std::string costName = nl->SymbolValue(costsArg);
|
|
int succCostsIndex = listutils::findAttribute(succAttrList,costName,
|
|
attrType);
|
|
if(!succCostsIndex){
|
|
return listutils::typeError("Attribute " + costName + " not found in "
|
|
"successor tuple");
|
|
}
|
|
if(!CcReal::checkType(attrType)){
|
|
return listutils::typeError("Attribute " + costName + " not of type "
|
|
"real in successor tuple");
|
|
}
|
|
ListExpr appendList;
|
|
if(nl->HasLength(args,7)){
|
|
appendList = nl->FourElemList(
|
|
nl->RealAtom(-1),
|
|
nl->IntAtom(succIndex -1),
|
|
nl->IntAtom(targetIndex -1),
|
|
nl->IntAtom(succCostsIndex-1));
|
|
|
|
} else {
|
|
appendList = nl->ThreeElemList(
|
|
nl->IntAtom(succIndex -1),
|
|
nl->IntAtom(targetIndex -1),
|
|
nl->IntAtom(succCostsIndex-1));
|
|
}
|
|
return nl->ThreeElemList(
|
|
nl->SymbolAtom(Symbols::APPEND()),
|
|
appendList,
|
|
res);
|
|
}
|
|
|
|
// costs are given by a function
|
|
if(!listutils::isMap<1>(costsArg)){
|
|
return listutils::typeError("sixth arg is not an unary function");
|
|
}
|
|
if(!CcReal::checkType(nl->Third(costsArg))){
|
|
return listutils::typeError("result of cost function not of type real");
|
|
}
|
|
ListExpr succTuple = nl->Second(succstream);
|
|
if(!nl->Equal(succTuple, nl->Second(costsArg))){
|
|
return listutils::typeError("successor tuple and argument of the cost "
|
|
"function differ");
|
|
}
|
|
ListExpr appendList;
|
|
if(nl->HasLength(args,7)){
|
|
appendList = nl->ThreeElemList(
|
|
nl->RealAtom(-1),
|
|
nl->IntAtom(succIndex -1),
|
|
nl->IntAtom(targetIndex -1));
|
|
} else {
|
|
appendList = nl->TwoElemList(
|
|
nl->IntAtom(succIndex -1),
|
|
nl->IntAtom(targetIndex -1));
|
|
}
|
|
return nl->ThreeElemList(
|
|
nl->SymbolAtom(Symbols::APPEND()),
|
|
appendList,
|
|
res);
|
|
}
|
|
|
|
|
|
template ListExpr mtMinPathCostsTM<true>(ListExpr args);
|
|
template ListExpr mtMinPathCostsTM<false>(ListExpr args);
|
|
|
|
|
|
template <class T>
|
|
class mtMinPathCostsInfo{
|
|
public:
|
|
mtMinPathCostsInfo(Supplier _succFun,
|
|
Word& _targetStream,
|
|
int _succIndex,
|
|
T* _source,
|
|
int _targetIndex,
|
|
int _costsIndex,
|
|
size_t _maxHops,
|
|
double _maxCosts,
|
|
TupleType* _tt):
|
|
succFun(_succFun),succIndex(_succIndex),
|
|
targetIndex(_targetIndex),
|
|
costsIndex(_costsIndex), maxHops(_maxHops),
|
|
maxCosts(_maxCosts),
|
|
tt(_tt){
|
|
if(maxCosts<=0){
|
|
maxCosts = std::numeric_limits<double>::max();
|
|
}
|
|
succArg = qp->Argument(succFun);
|
|
tt->IncReference();
|
|
costFun = 0;
|
|
collectTargets(_targetStream);
|
|
queueEntry<T> qe((T*)_source->Copy(), 0,0);
|
|
front.push(qe);
|
|
|
|
|
|
//noprocessedNodes = 0;
|
|
//noreprocessedNodes = 0;
|
|
//noprocessedEdges = 0;
|
|
//maxFront = 1;
|
|
|
|
compute();
|
|
resPos = 0;
|
|
}
|
|
|
|
|
|
mtMinPathCostsInfo(Supplier _succFun,
|
|
Word& _targetStream,
|
|
int _succIndex,
|
|
T* _source,
|
|
int _targetIndex,
|
|
Supplier _costsFun,
|
|
int _maxHops,
|
|
double _maxCosts,
|
|
TupleType* _tt):
|
|
succFun(_succFun),succIndex(_succIndex),
|
|
targetIndex(_targetIndex),
|
|
costsIndex(-1), maxHops(_maxHops),
|
|
maxCosts(_maxCosts),
|
|
tt(_tt){
|
|
if(maxCosts<=0){
|
|
maxCosts = std::numeric_limits<double>::max();
|
|
}
|
|
costFun = _costsFun;
|
|
succArg = qp->Argument(succFun);
|
|
tt->IncReference();
|
|
costFunArg = qp->Argument(costFun);
|
|
|
|
collectTargets(_targetStream);
|
|
queueEntry<T> qe((T*)_source->Copy(), 0,0);
|
|
front.push(qe);
|
|
|
|
//noprocessedNodes = 0;
|
|
//noreprocessedNodes = 0;
|
|
//noprocessedEdges = 0;
|
|
//maxFront = 1;
|
|
|
|
compute();
|
|
resPos = 0;
|
|
}
|
|
|
|
~mtMinPathCostsInfo(){
|
|
|
|
/*if(noprocessedEdges>130){
|
|
cout << "processedNodes " << noprocessedNodes << endl;
|
|
cout << "reporcessedNodes " << noreprocessedNodes << endl;
|
|
cout << "processedEdges " << noprocessedEdges << endl;
|
|
cout << "maxFront " << maxFront << endl << endl;
|
|
}
|
|
*/
|
|
|
|
|
|
// delete remaining tuples in front
|
|
while(!front.empty()){
|
|
front.top().node->DeleteIfAllowed();
|
|
front.pop();
|
|
}
|
|
// delete remaining tuples from results
|
|
while(resPos < results.size()){
|
|
results[resPos++]->DeleteIfAllowed();
|
|
}
|
|
results.clear();
|
|
// all targets should be tranferred into
|
|
// result, possible with maximum costs
|
|
assert(targets.IsEmpty());
|
|
tt->DeleteIfAllowed();
|
|
}
|
|
|
|
Tuple* next(){
|
|
if(resPos>=results.size()){
|
|
return 0;
|
|
}
|
|
Tuple* res = results[resPos];
|
|
results[resPos]=0;
|
|
resPos++;
|
|
return res;
|
|
}
|
|
|
|
|
|
private:
|
|
Supplier succFun;
|
|
int succIndex;
|
|
int targetIndex;
|
|
int costsIndex;
|
|
size_t maxHops;
|
|
double maxCosts;
|
|
TupleType* tt;
|
|
|
|
Supplier costFun;
|
|
ArgVectorPointer costFunArg;
|
|
|
|
//size_t noprocessedNodes;
|
|
//size_t noreprocessedNodes;
|
|
//size_t noprocessedEdges;
|
|
//size_t maxFront;
|
|
|
|
|
|
|
|
typedef typename T::ctype ct;
|
|
typedef avltree::AVLTree<ct> set_T;
|
|
|
|
|
|
struct costentry{
|
|
costentry(ct _n, double _c): node(_n), cost(_c){}
|
|
ct node;
|
|
double cost;
|
|
|
|
};
|
|
class costentryComp{
|
|
public:
|
|
static bool smaller(const costentry& o1, const costentry& o2){
|
|
return o1.node < o2.node;
|
|
}
|
|
static bool equal(const costentry& o1, const costentry& o2){
|
|
return o1.node == o2.node;
|
|
}
|
|
static bool greater(const costentry& o1, const costentry& o2){
|
|
return o1.node > o2.node;
|
|
}
|
|
};
|
|
|
|
|
|
//typedef std::map<ct,double> frontCost_T;
|
|
typedef avltree::AVLTree<costentry,costentryComp> frontCost_T;
|
|
|
|
|
|
struct targetentry{
|
|
targetentry(ct _n, Tuple* _t): node(_n),tuple(_t){}
|
|
/*
|
|
static void kill(targetentry& e){
|
|
e.tuple->DeleteIfAllowed();
|
|
e.tuple = 0;
|
|
}
|
|
*/
|
|
ct node;
|
|
Tuple* tuple;
|
|
};
|
|
|
|
class targetentryComp{
|
|
public:
|
|
static bool smaller(const targetentry& o1, const targetentry& o2){
|
|
return o1.node < o2.node;
|
|
}
|
|
static bool equal(const targetentry& o1, const targetentry& o2){
|
|
return o1.node == o2.node;
|
|
}
|
|
static bool greater(const targetentry& o1, const targetentry& o2){
|
|
return o1.node > o2.node;
|
|
}
|
|
};
|
|
|
|
typedef avltree::AVLTree<targetentry,targetentryComp> targets_T;
|
|
|
|
ArgVectorPointer succArg;
|
|
targets_T targets;
|
|
std::vector<Tuple*> results;
|
|
|
|
|
|
typedef mmheap::mmheap<queueEntry<T>, queueentryComp> prio_queue_t;
|
|
//typedef std::priority_queue<queueEntry<T> > prio_queue_t;
|
|
prio_queue_t front;
|
|
set_T processedNodes;
|
|
double targetCosts;
|
|
frontCost_T frontCosts;
|
|
|
|
size_t resPos;
|
|
|
|
|
|
|
|
/*
|
|
~collect targets~
|
|
|
|
Reads all tuples from the stream of targets and inserts them
|
|
into the targets map.
|
|
|
|
|
|
*/
|
|
void collectTargets(Word& s){
|
|
|
|
size_t failed = 0;
|
|
Stream<Tuple> stream(s);
|
|
stream.open();
|
|
Tuple* tuple;
|
|
bool found;
|
|
while( (tuple=stream.request())!=0){
|
|
T* target = ((T*) tuple->GetAttribute(targetIndex));
|
|
if(!target->IsDefined()){
|
|
std::cerr << "found undefined target" << std::endl;
|
|
tuple->DeleteIfAllowed();
|
|
} else {
|
|
ct node = target->GetValue();
|
|
targetentry e(node,tuple);
|
|
targets.insert3(e,found);
|
|
if(found){ // target already there
|
|
tuple->DeleteIfAllowed();
|
|
failed++;
|
|
}
|
|
}
|
|
}
|
|
stream.close();
|
|
|
|
/*
|
|
if(failed>0){
|
|
cout << "found some targets more than one time " << endl;
|
|
cout << "found " << targets.size() << " unique targets" << endl;
|
|
cout << "ignored " << failed
|
|
<< " targets which was given more than once" << endl;
|
|
}
|
|
*/
|
|
}
|
|
|
|
/*
|
|
~compute~
|
|
|
|
This function computes the result set. The result set is
|
|
available in the results vector.
|
|
|
|
*/
|
|
void compute(){
|
|
dijkstra();
|
|
|
|
// may be there are nodes that are not found.
|
|
// such nodes are inserted into the result set
|
|
// with maximum costs
|
|
typename targets_T::iterator it = targets.begin();
|
|
double c = std::numeric_limits<double>::max();
|
|
while(!it.onEnd()){
|
|
Tuple* t = it.Get()->tuple;
|
|
assert(t);
|
|
results.push_back(createResultTuple(t,c));
|
|
t->DeleteIfAllowed();
|
|
it.Get()->tuple=0;
|
|
it++;
|
|
}
|
|
targets.Empty();
|
|
|
|
}
|
|
|
|
Tuple* createResultTuple(const Tuple* t, double c){
|
|
Tuple* res = new Tuple(tt);
|
|
assert(t);
|
|
for(int i=0;i<t->GetNoAttributes();i++){
|
|
res->CopyAttribute(i,t,i);
|
|
}
|
|
res->PutAttribute(t->GetNoAttributes(), new CcReal(true,c));
|
|
return res;
|
|
}
|
|
|
|
|
|
void dijkstra(){
|
|
while(!front.empty() && !targets.IsEmpty()){
|
|
queueEntry<T> e = front.top();
|
|
front.pop();
|
|
processNode(e);
|
|
e.node->DeleteIfAllowed();
|
|
}
|
|
}
|
|
|
|
void processNode(queueEntry<T>& e){
|
|
if(processedNodes.member(e.nodeValue)){
|
|
//noreprocessedNodes++;
|
|
return;
|
|
}
|
|
//noprocessedNodes++;
|
|
processedNodes.insert(e.nodeValue);
|
|
|
|
targetentry dummy(e.nodeValue,0);
|
|
targetentry* te = targets.getMember(dummy);
|
|
|
|
if(te){
|
|
// found a target node
|
|
results.push_back(createResultTuple(te->tuple,e.costs));
|
|
te->tuple->DeleteIfAllowed();
|
|
targets.remove(dummy);
|
|
if(targets.IsEmpty()){ // all targets reached
|
|
return;
|
|
}
|
|
}
|
|
|
|
if(maxHops>0 && e.depth>=maxHops){
|
|
return;
|
|
}
|
|
if(e.costs>maxCosts){
|
|
// threshold reached
|
|
return;
|
|
}
|
|
|
|
// use successors of e node
|
|
(*succArg)[0] = e.node;
|
|
qp->Open(succFun);
|
|
Word value;
|
|
qp->Request(succFun,value);
|
|
while(qp->Received(succFun)){
|
|
processEdge(e, (Tuple*) value.addr);
|
|
qp->Request(succFun,value);
|
|
}
|
|
qp->Close(succFun);
|
|
/*
|
|
if(front.size()>maxFront){
|
|
maxFront = front.size();
|
|
}
|
|
*/
|
|
}
|
|
|
|
|
|
void processEdge(queueEntry<T>& e, Tuple* tup){
|
|
//noprocessedEdges++;
|
|
double costs = getCosts(tup);
|
|
// special case: invalid costs
|
|
if(costs<0){
|
|
std::cerr << "found negative or undefined costs" << std::endl;
|
|
tup->DeleteIfAllowed();
|
|
return;
|
|
}
|
|
T* target = (T*) tup->GetAttribute(targetIndex);
|
|
// special case undefined value
|
|
if(!target->IsDefined()){
|
|
std::cerr << " found undefined costs" << endl;
|
|
tup->DeleteIfAllowed();
|
|
return;
|
|
}
|
|
ct targetVal = target->GetValue();
|
|
// target already finished
|
|
if(processedNodes.member(targetVal)){
|
|
tup->DeleteIfAllowed();
|
|
return;
|
|
}
|
|
target = (T*) target->Copy();
|
|
tup->DeleteIfAllowed();
|
|
double nc = e.costs + costs;
|
|
|
|
|
|
bool found;
|
|
costentry e1(targetVal, nc);
|
|
costentry* e2 = frontCosts.insert3(e1,found);
|
|
|
|
if(!found){ // node found the first time
|
|
front.push(queueEntry<T>(target, nc, e.depth+1));
|
|
} else {
|
|
if(e2->cost <= nc ){ // old path is shorter
|
|
target->DeleteIfAllowed();
|
|
} else {
|
|
e2->cost = nc; // update costs
|
|
front.push(queueEntry<T>(target, nc, e.depth+1));
|
|
}
|
|
}
|
|
}
|
|
|
|
double getCosts(Tuple* t){
|
|
if(costFun){
|
|
return getCostsFun(t);
|
|
} else {
|
|
return getCostsAttr(t);
|
|
}
|
|
}
|
|
|
|
double getCostsFun(Tuple* t){
|
|
(*costFunArg)[0].addr = t;
|
|
Word value;
|
|
qp->Request(costFun, value);
|
|
CcReal* g = (CcReal*) value.addr;
|
|
bool correct = g->IsDefined();
|
|
(*costFunArg)[0].addr = 0;
|
|
if(!correct){
|
|
return -1;
|
|
}
|
|
return g->GetValue();
|
|
}
|
|
|
|
double getCostsAttr(Tuple* t){
|
|
CcReal* g = (CcReal*) t->GetAttribute(costsIndex);
|
|
bool correct = g->IsDefined();
|
|
if(!correct){
|
|
return -1;
|
|
}
|
|
return g->GetValue();
|
|
}
|
|
};
|
|
|
|
|
|
|
|
template<class T>
|
|
int mtMinPathCost1VMT(Word* args, Word& result, int message,
|
|
Word& local, Supplier s) {
|
|
|
|
mtMinPathCostsInfo<T>* li = (mtMinPathCostsInfo<T>*) local.addr;
|
|
TupleType* tt = (TupleType*) qp->GetLocal2(s).addr;
|
|
switch(message){
|
|
case INIT : {
|
|
tt = new TupleType(nl->Second(GetTupleResultType(s)));
|
|
qp->GetLocal2(s).addr = tt;
|
|
return 0;
|
|
}
|
|
case FINISH: {
|
|
if(tt){
|
|
tt->DeleteIfAllowed();
|
|
qp->GetLocal2(s).addr=0;
|
|
}
|
|
return 0;
|
|
}
|
|
case OPEN: {
|
|
if(li){
|
|
delete li;
|
|
local.addr = 0;
|
|
}
|
|
T* source = (T*) args[3].addr;
|
|
if(!source->IsDefined()){
|
|
return 0;
|
|
}
|
|
int hops = 0;
|
|
CcInt* Hops = (CcInt*) args[6].addr;
|
|
if(Hops->IsDefined()){
|
|
hops = Hops->GetValue();
|
|
if(hops<0){
|
|
hops = 0;
|
|
}
|
|
}
|
|
double mc = -1;
|
|
CcReal* maxCosts = (CcReal*)args[7].addr;
|
|
if(maxCosts->IsDefined()){
|
|
mc = maxCosts->GetValue();
|
|
}
|
|
int succIndex = ((CcInt*) args[8].addr)->GetValue();
|
|
int targetIndex = ((CcInt*) args[9].addr)->GetValue();
|
|
int costsIndex = ((CcInt*) args[10].addr)->GetValue();
|
|
li = new mtMinPathCostsInfo<T>(args[0].addr, args[1],
|
|
succIndex, source, targetIndex,
|
|
costsIndex, hops,mc,tt);
|
|
local.addr = li;
|
|
return 0;
|
|
}
|
|
case REQUEST: {
|
|
result.addr = li?li->next():0;
|
|
return result.addr?YIELD:CANCEL;
|
|
}
|
|
case CLOSE: {
|
|
if(li){
|
|
delete li;
|
|
local.addr = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
template<class T>
|
|
int mtMinPathCost2VMT(Word* args, Word& result, int message,
|
|
Word& local, Supplier s) {
|
|
|
|
mtMinPathCostsInfo<T>* li = (mtMinPathCostsInfo<T>*) local.addr;
|
|
TupleType* tt = (TupleType*) qp->GetLocal2(s).addr;
|
|
switch(message){
|
|
case INIT : {
|
|
tt = new TupleType(nl->Second(GetTupleResultType(s)));
|
|
qp->GetLocal2(s).addr = tt;
|
|
return 0;
|
|
}
|
|
case FINISH: {
|
|
if(tt){
|
|
tt->DeleteIfAllowed();
|
|
qp->GetLocal2(s).addr=0;
|
|
}
|
|
return 0;
|
|
}
|
|
case OPEN: {
|
|
if(li){
|
|
delete li;
|
|
local.addr = 0;
|
|
}
|
|
T* source = (T*) args[3].addr;
|
|
if(!source->IsDefined()){
|
|
return 0;
|
|
}
|
|
int hops = 0;
|
|
CcInt* Hops = (CcInt*) args[6].addr;
|
|
if(Hops->IsDefined()){
|
|
hops = Hops->GetValue();
|
|
if(hops<0){
|
|
hops=0;
|
|
}
|
|
}
|
|
double mc = -1;
|
|
CcReal* maxCosts = (CcReal*)args[7].addr;
|
|
if(maxCosts->IsDefined()){
|
|
mc = maxCosts->GetValue();
|
|
}
|
|
|
|
int succIndex = ((CcInt*) args[8].addr)->GetValue();
|
|
int targetIndex = ((CcInt*) args[9].addr)->GetValue();
|
|
Supplier costsFun = args[5].addr;
|
|
li = new mtMinPathCostsInfo<T>(args[0].addr, args[1],
|
|
succIndex, source, targetIndex,
|
|
costsFun, hops,mc, tt);
|
|
local.addr = li;
|
|
return 0;
|
|
}
|
|
case REQUEST: {
|
|
result.addr = li?li->next():0;
|
|
return result.addr?YIELD:CANCEL;
|
|
}
|
|
case CLOSE: {
|
|
if(li){
|
|
delete li;
|
|
local.addr = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
template int mtMinPathCost1VMT<CcInt>(Word* args, Word& result, int message,
|
|
Word& local, Supplier s);
|
|
|
|
template int mtMinPathCost1VMT<LongInt>(Word* args, Word& result, int message,
|
|
Word& local, Supplier s);
|
|
|
|
|
|
template int mtMinPathCost2VMT<CcInt>(Word* args, Word& result, int message,
|
|
Word& local, Supplier s);
|
|
|
|
template int mtMinPathCost2VMT<LongInt>(Word* args, Word& result, int message,
|
|
Word& local, Supplier s);
|
|
|
|
/*
|
|
3 Implementation of a bidirectional dijkstra.
|
|
|
|
This operator searches a shortest path between a source and a target node.
|
|
In contrast to a normal implementation of dijktra, this operator searches
|
|
bidirectional, i.e. from the source and the target at the same time.
|
|
|
|
3.1 Type Mapping
|
|
|
|
Function returning the successor of a node (tuple edge)
|
|
|
|
Function returning predecessor of a node (tuple edge)
|
|
|
|
Attribute name of the successor
|
|
|
|
attribute name of the predecessor
|
|
|
|
source node
|
|
|
|
target node
|
|
|
|
cost function: (edge tuple -> real)
|
|
|
|
*/
|
|
|
|
ListExpr gbidijkstraTM(ListExpr args){
|
|
if(!nl->HasLength(args,7)){
|
|
return listutils::typeError("7 arguments required");
|
|
}
|
|
|
|
ListExpr fun1 = nl->First(args);
|
|
if(!listutils::isMap<1>(fun1)){
|
|
return listutils::typeError("first arg is not a unary function");
|
|
}
|
|
ListExpr fun2 = nl->Second(args);
|
|
if(!listutils::isMap<1>(fun2)){
|
|
return listutils::typeError("second arg is not a unary function");
|
|
}
|
|
if(!nl->Equal(fun1,fun2)){
|
|
return listutils::typeError("function types must be equal");
|
|
}
|
|
|
|
// we allow int, string and longint as node type
|
|
ListExpr source = nl->Fifth(args);
|
|
if( !CcInt::checkType(source)
|
|
&& !LongInt::checkType(source)){
|
|
return listutils::typeError("allowed node types are int, longint");
|
|
}
|
|
if(!nl->Equal(source,nl->Sixth(args))){
|
|
return listutils::typeError("source node and target node "
|
|
"have different types");
|
|
}
|
|
// check function argument
|
|
ListExpr funarg = nl->Second(fun1);
|
|
if(!nl->Equal(funarg,source)){
|
|
return listutils::typeError("successor function's argument and "
|
|
"source node type differ");
|
|
}
|
|
// result of the function must be a tuple stream
|
|
ListExpr funres = nl->Third(fun1);
|
|
if(!Stream<Tuple>::checkType(funres)){
|
|
return listutils::typeError("successors function's return type "
|
|
"is not a tuple stream");
|
|
}
|
|
ListExpr attrList = nl->Second(nl->Second(funres));
|
|
|
|
// the third argument must be an attribute in the edge tuple
|
|
ListExpr succNameL = nl->Third(args);
|
|
if(nl->AtomType(succNameL) != SymbolType){
|
|
return listutils::typeError("third argument is not a valid "
|
|
"attribute name");
|
|
}
|
|
std::string succName = nl->SymbolValue(succNameL);
|
|
ListExpr attrType;
|
|
int index1 = listutils::findAttribute(attrList, succName, attrType);
|
|
if(!index1){
|
|
return listutils::typeError("Attribute " + succName
|
|
+ " is not part of the edge tuple");
|
|
}
|
|
if(!nl->Equal(attrType,source)){
|
|
return listutils::typeError("Type of attribute " + succName
|
|
+" and type of source node differ");
|
|
}
|
|
// the same must be checked for the name of the predecessor
|
|
ListExpr predNameL = nl->Fourth(args);
|
|
if(nl->AtomType(predNameL) != SymbolType){
|
|
return listutils::typeError("fourth attribute is not a valid "
|
|
"attribute name");
|
|
}
|
|
std::string predName = nl->SymbolValue(predNameL);
|
|
int index2 = listutils::findAttribute(attrList, predName, attrType);
|
|
if(!index2){
|
|
return listutils::typeError("attribute " + predName
|
|
+ " not part of the edge tuple");
|
|
}
|
|
if(!nl->Equal(attrType,source)){
|
|
return listutils::typeError("attribute " + predName
|
|
+" has another type than the nodes ");
|
|
}
|
|
|
|
// check whether the 7th argument is a function from an
|
|
// edge tuple to real
|
|
ListExpr costFun = nl->Sixth(nl->Rest(args));
|
|
if(!listutils::isMap<1>(costFun)){
|
|
return listutils::typeError("cost fun is not a unary function");
|
|
}
|
|
ListExpr edgeTuple = nl->Second(funres);
|
|
if(!nl->Equal(nl->Second(costFun),edgeTuple)){
|
|
return listutils::typeError("edge tuple and cost fun argument differ");
|
|
}
|
|
if(!CcReal::checkType(nl->Third(costFun))){
|
|
return listutils::typeError("result of the cost function is not a real");
|
|
}
|
|
|
|
if(listutils::findAttribute(attrList,"IsForward",attrType)){
|
|
return listutils::typeError("edge tuple contains a IsForward attribute");
|
|
}
|
|
ListExpr add = nl->OneElemList(nl->TwoElemList(
|
|
nl->SymbolAtom("IsForward"),
|
|
listutils::basicSymbol<CcBool>()));
|
|
ListExpr resAttrList = listutils::concat(attrList,add);
|
|
|
|
if(!listutils::isAttrList(resAttrList)){
|
|
return listutils::typeError("Unknown error, may be a stupid programmer");
|
|
}
|
|
|
|
return nl->ThreeElemList(
|
|
nl->SymbolAtom(Symbols::APPEND()),
|
|
nl->TwoElemList(
|
|
nl->IntAtom(index1 - 1),
|
|
nl->IntAtom(index2 - 1)),
|
|
nl->TwoElemList(
|
|
listutils::basicSymbol<Stream<Tuple> >(),
|
|
nl->TwoElemList(
|
|
listutils::basicSymbol<Tuple>(),
|
|
resAttrList)));
|
|
}
|
|
|
|
|
|
template<class T>
|
|
class gbidijkstraInfo{
|
|
|
|
public:
|
|
gbidijkstraInfo(
|
|
Supplier _succFun,
|
|
Supplier _predFun,
|
|
int _succPos,
|
|
int _predPos,
|
|
Supplier _costFun,
|
|
T* _source,
|
|
T* _target,
|
|
TupleType* _tt):
|
|
succFun(_succFun), predFun(_predFun),
|
|
succPos(_succPos), predPos(_predPos),
|
|
costFun(_costFun),
|
|
source(_source), target(_target),
|
|
tt(_tt){
|
|
tt->IncReference();
|
|
succArg = qp->Argument(succFun);
|
|
predArg = qp->Argument(predFun);
|
|
costArg = qp->Argument(costFun);
|
|
commonNode = 0;
|
|
minCosts = 0;
|
|
Ffront.push(queueEntry<T>(source,0,0));
|
|
Bfront.push(queueEntry<T>(target,0,0));
|
|
dijkstra();
|
|
targetValue = target->GetValue();
|
|
if(commonNode){
|
|
currentNode = commonNode->GetValue();
|
|
fillForwardTuples();
|
|
}
|
|
}
|
|
|
|
~gbidijkstraInfo(){
|
|
tt->DeleteIfAllowed();
|
|
removeEdges(Ftree);
|
|
removeEdges(Btree);
|
|
while(!forwardTuples.empty()){
|
|
forwardTuples.top()->DeleteIfAllowed();
|
|
forwardTuples.pop();
|
|
}
|
|
}
|
|
|
|
Tuple* next(){
|
|
if(!commonNode){
|
|
return 0;
|
|
}
|
|
if(!forwardTuples.empty()){
|
|
Tuple* res = forwardTuples.top();
|
|
forwardTuples.pop();
|
|
return res;
|
|
} else {
|
|
if(targetValue == currentNode){
|
|
// end reached
|
|
commonNode = 0;
|
|
return 0;
|
|
}
|
|
tree_it it = Btree.find(currentNode);
|
|
assert(it!=Btree.end());
|
|
Tuple* res = createResultTuple(it->second,false);
|
|
currentNode = it->second.pred;
|
|
return res;
|
|
}
|
|
}
|
|
|
|
|
|
private:
|
|
|
|
typedef typename T::ctype ct;
|
|
|
|
Supplier succFun;
|
|
Supplier predFun;
|
|
int succPos;
|
|
int predPos;
|
|
Supplier costFun;
|
|
T* source;
|
|
T* target;
|
|
ct targetValue;
|
|
TupleType* tt;
|
|
ArgVectorPointer succArg;
|
|
ArgVectorPointer predArg;
|
|
ArgVectorPointer costArg;
|
|
T* commonNode;
|
|
double minCosts;
|
|
|
|
typedef mmheap::mmheap<queueEntry<T>, queueentryComp> prio_queue_t;
|
|
//typedef std::priority_queue<queueEntry<T> > prio_queue_t;
|
|
prio_queue_t Ffront;
|
|
prio_queue_t Bfront;
|
|
typedef std::map<ct,treeEntry<ct> > tree_t;
|
|
typedef typename tree_t::iterator tree_it;
|
|
tree_t Ftree;
|
|
tree_t Btree;
|
|
|
|
typedef std::map<ct,double> finish_t;
|
|
typedef typename finish_t::iterator finish_it;
|
|
|
|
finish_t Ffinished;
|
|
finish_t Bfinished;
|
|
|
|
ct currentNode; // for tracing the path
|
|
|
|
// to return the path in a 'normal' order from source
|
|
// to target, we put the tuples from fromward tree to
|
|
// a stack
|
|
std::stack<Tuple*> forwardTuples;
|
|
|
|
|
|
|
|
void removeEdges(tree_t& tree){
|
|
for(tree_it it = tree.begin();it!=tree.end();it++){
|
|
Tuple* edge = it->second.edge;
|
|
assert(edge);
|
|
edge->DeleteIfAllowed();
|
|
it->second.edge=0;
|
|
}
|
|
}
|
|
|
|
|
|
void fillForwardTuples(){
|
|
if(!commonNode){
|
|
return;
|
|
}
|
|
currentNode = commonNode->GetValue();
|
|
ct sn = source->GetValue();
|
|
tree_it it;
|
|
while(sn != currentNode){
|
|
it = Ftree.find(currentNode);
|
|
assert(it!=Ftree.end());
|
|
treeEntry<ct>& e = it->second;
|
|
assert(e.edge);
|
|
Tuple* res = createResultTuple(e,true);
|
|
currentNode = it->second.pred;
|
|
forwardTuples.push(res);
|
|
}
|
|
// prepare back direction
|
|
currentNode = commonNode->GetValue();
|
|
}
|
|
|
|
void dijkstra(){
|
|
|
|
while(!Ffront.empty() || !Bfront.empty()){
|
|
if(commonNode){
|
|
if(!Ffront.empty() && Ffront.top().costs>=minCosts){
|
|
makeempty(Ffront);
|
|
}
|
|
if(!Bfront.empty() && Bfront.top().costs>=minCosts){
|
|
makeempty(Bfront);
|
|
}
|
|
if(Ffront.empty() && Bfront.empty()){
|
|
return;
|
|
}
|
|
}
|
|
if(Ffront.empty()){
|
|
if(Bfront.empty()){
|
|
// no better solution to find
|
|
return;
|
|
} else {
|
|
// only back front available
|
|
queueEntry<T> e = Bfront.top();
|
|
Bfront.pop();
|
|
processNode(e,false);
|
|
}
|
|
} else {
|
|
if(Bfront.empty()){
|
|
// only forward front not empty
|
|
queueEntry<T> e = Ffront.top();
|
|
Ffront.pop();
|
|
processNode(e,true);
|
|
} else {
|
|
// both fronts have elements
|
|
if(Ffront.top().costs<=Bfront.top().costs){
|
|
queueEntry<T> e = Ffront.top();
|
|
Ffront.pop();
|
|
processNode(e,true);
|
|
} else {
|
|
queueEntry<T> e = Bfront.top();
|
|
Bfront.pop();
|
|
processNode(e,false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void makeempty(prio_queue_t& q){
|
|
while(!q.empty()){
|
|
q.pop();
|
|
}
|
|
}
|
|
|
|
void processNode(queueEntry<T>& e, bool isForward){
|
|
// check stop criterion
|
|
ct v = e.nodeValue;
|
|
Supplier fun;
|
|
ArgVectorPointer arg;
|
|
int pos;
|
|
if(isForward){
|
|
if(Ffinished.find(v)!=Ffinished.end()){
|
|
// node already processed
|
|
return;
|
|
}
|
|
Ffinished[v] = e.costs;
|
|
finish_it fit = Bfinished.find(v);
|
|
if(fit!=Bfinished.end()){
|
|
// found common green node
|
|
double fc = fit->second;
|
|
if(!commonNode || minCosts>e.costs+fc){
|
|
// found common node the first time or shorter path
|
|
commonNode = e.node;
|
|
minCosts = e.costs + fc;
|
|
}
|
|
}
|
|
// further search
|
|
fun = succFun;
|
|
arg = succArg;
|
|
pos = succPos;
|
|
} else {
|
|
if(Bfinished.find(v)!=Bfinished.end()){
|
|
// node already processed
|
|
return;
|
|
}
|
|
Bfinished[v] = e.costs;
|
|
finish_it fit = Ffinished.find(v);
|
|
if(fit!=Ffinished.end()){
|
|
// found common green node
|
|
double fc = fit->second;
|
|
if(!commonNode || minCosts>e.costs+fc){
|
|
// found common node the first time or shorter path
|
|
commonNode = e.node;
|
|
minCosts = e.costs + fc;
|
|
}
|
|
}
|
|
fun = predFun;
|
|
arg = predArg;
|
|
pos = predPos;
|
|
}
|
|
|
|
// process all edges
|
|
(*arg)[0] = e.node;
|
|
Word f(fun);
|
|
Stream<Tuple> stream(f);
|
|
Tuple* edge;
|
|
stream.open();
|
|
while( (edge = stream.request()) ){
|
|
processEdge(e.nodeValue, e.costs, edge, isForward, pos, e.depth);
|
|
}
|
|
stream.close();
|
|
}
|
|
|
|
void processEdge(ct pred, double costs,
|
|
Tuple* edge, bool isForward, int pos,
|
|
int depth){
|
|
|
|
assert(edge);
|
|
|
|
T* target = (T*) edge->GetAttribute(pos);
|
|
if(!target->IsDefined()){ // ignore nonsense tuples
|
|
edge->DeleteIfAllowed();
|
|
return;
|
|
}
|
|
ct v = target->GetValue();
|
|
finish_t* finished = isForward?&Ffinished:&Bfinished;
|
|
tree_t* tree = isForward?&Ftree:&Btree;
|
|
prio_queue_t* front = isForward?&Ffront:&Bfront;
|
|
|
|
if(finished->find(v)!=finished->end()){
|
|
// target node already processed
|
|
edge->DeleteIfAllowed();
|
|
return;
|
|
}
|
|
|
|
double edgeCosts = getCosts(edge);
|
|
if(edgeCosts < 0){ // invalid costs
|
|
edge->DeleteIfAllowed();
|
|
return;
|
|
}
|
|
|
|
|
|
double newCosts = costs + edgeCosts;
|
|
tree_it it = tree->find(v);
|
|
if(it==tree->end()){ // node reached the first time
|
|
treeEntry<ct> e(pred,newCosts,depth + 1, edge);
|
|
(*tree)[v] = e;
|
|
queueEntry<T> qe(target, newCosts,depth+1);
|
|
front->push(qe);
|
|
return;
|
|
}
|
|
|
|
if(it->second.costs<=newCosts){
|
|
// new path is not shorter than an old one
|
|
edge->DeleteIfAllowed();
|
|
return;
|
|
}
|
|
|
|
// update case
|
|
it->second.edge->DeleteIfAllowed();
|
|
it->second.edge = edge;
|
|
it->second.pred = pred;
|
|
it->second.costs = newCosts;
|
|
it->second.depth = depth+1;
|
|
queueEntry<T> qe(target, newCosts,depth+1);
|
|
front->push(qe);
|
|
}
|
|
|
|
|
|
double getCosts(Tuple* edge) const{
|
|
(*costArg)[0] = edge;
|
|
Word value;
|
|
qp->Request(costFun, value);
|
|
CcReal* g = (CcReal*) value.addr;
|
|
if(!g->IsDefined()){
|
|
return -1;
|
|
}
|
|
return g->GetValue();
|
|
}
|
|
|
|
Tuple* createResultTuple(const treeEntry<ct>& e, bool forward){
|
|
Tuple* res = new Tuple(tt);
|
|
Tuple* src = e.edge;
|
|
assert(src);
|
|
for(int i=0;i<src->GetNoAttributes();i++){
|
|
res->CopyAttribute(i,src,i);
|
|
}
|
|
res->PutAttribute(src->GetNoAttributes(),new CcBool(true,forward));
|
|
return res;
|
|
}
|
|
};
|
|
|
|
|
|
|
|
template<class T>
|
|
int gbidijkstraVMT(Word* args, Word& result, int message,
|
|
Word& local, Supplier s) {
|
|
|
|
gbidijkstraInfo<T>* li = (gbidijkstraInfo<T>*) local.addr;
|
|
TupleType* tt = (TupleType*) qp->GetLocal2(s).addr;
|
|
|
|
switch(message){
|
|
case INIT : {
|
|
tt = new TupleType(nl->Second(GetTupleResultType(s)));
|
|
qp->GetLocal2(s).addr = tt;
|
|
return 0;
|
|
}
|
|
case FINISH: {
|
|
if(tt){
|
|
tt->DeleteIfAllowed();
|
|
qp->GetLocal2(s).addr=0;
|
|
}
|
|
return 0;
|
|
}
|
|
case OPEN:{
|
|
if(li){
|
|
delete li;
|
|
local.addr = 0;
|
|
}
|
|
int succPos = ((CcInt*) args[7].addr)->GetValue();
|
|
int predPos = ((CcInt*) args[8].addr)->GetValue();
|
|
T* source = (T*) args[4].addr;
|
|
T* target = (T*) args[5].addr;
|
|
if(!source->IsDefined() || !target->IsDefined()){
|
|
return 0;
|
|
}
|
|
if(source->Compare(target)==0){
|
|
return 0;
|
|
}
|
|
local.addr = new gbidijkstraInfo<T>(args[0].addr, args[1].addr,succPos,
|
|
predPos, args[6].addr, source,
|
|
target, tt);
|
|
return 0;
|
|
}
|
|
|
|
case REQUEST: {
|
|
result.addr = li?li->next():0;
|
|
return result.addr?YIELD:CANCEL;
|
|
}
|
|
case CLOSE: {
|
|
if(li){
|
|
delete li;
|
|
local.addr = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
template int gbidijkstraVMT<CcInt>(Word* args, Word& result, int message,
|
|
Word& local, Supplier s);
|
|
|
|
|
|
template int gbidijkstraVMT<LongInt>(Word* args, Word& result, int message,
|
|
Word& local, Supplier s);
|
|
|
|
|
|
|
|
} // end of namespace general_dijkstra
|
|
|
|
|
|
|
|
|