Latest commit

History

65 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Discrete Optimal Search Library (DOSL)

subh83/DOSL

Description:

"Discrete Optimal Search Library (DOSL)" is a fast, efficient and easy-to-use library for construction of discrete representation (e.g., a graph or a simplicial complex) and search (e.g., using algorithms like A-star, Dijkstra's, etc.) library written in C++, designed specifically for searching medium to large scale graphs or simplicial complexes for optimal paths.


Output from example program map2d_PathPlanning showing the progress of A* search algorithm in finding shortest path in an 8-connected grid graph and using an Euclidean heuristic function (requires OpenCV):

5000 vertices expanded10000 vertices expanded15000 vertices expandedFinal path

See the Overview of Selected Example Programs for more sample outputs from the provided example programs.


DOSL is designed to be:

  • Fast (e.g., with integer coordinates for nodes but floating point cost as well as cost function needing to perform floating point operations online, and an average degree of the graph being 8, the A* search algorithm of the library can expand about 150,000 nodes in the graph in about 0.53 second on a 1.8GHz quad-core processor machine with 16GB RAM.)
  • Easy to use / versatile (being template-based, defining new arbitrary node-types, cost types, etc. is made easy. For graph connectivity, node accessibility tests, etc, user-defined classes can be used, which makes defining the graph structure very easy, yet highly flexible.)

DOSL supports:

  • Directed graphs with complex cost functions.
  • On the fly graph construction (i.e. no need to construct and store a complete graph before starting the search/planning process - makes it highly suitable for RRT-like graph construction).
  • Arbitrary graph node type declaration.
  • Arbitrary edge cost type declaration.
  • Intermediate storage of paths during graph search.
  • Multiple goals (or goal manifold) that determine when to stop search.
  • Multiple start nodes for wave-front expansion type graph exploration.
  • Event handling (i.e., call to user-defined functions upon generation, g-score updating or expansion of a node during the search process).
  • Other forms of discrete representations, such as simplicial complexes (S-star algorithm), is supported by specific planners.
  • Ability to write new planners with ease. Comes with a weighted A-star (that includes Dijkstra's and normal A-star), Theta-star and S-star planner by default.

New:

  • The [AlgName]::Algorithm class template now takes in the derived class as its first template parameter (a CRTP). Thus, definition of a searchProblem class should now be made as follows:
classsearchProblem : publicAStar::Algorithm <searchProblem, myNode, double>
{ /* ... */}; // ^^^^ new template parameter

This change (replacing virtual functions with CRTP) has helped some example programs to run twice as fast.
Backward compatibility note (since v3.3): Users need to make this change for backward compatibility.
See the example programs for illustration and details.

NOTE: Discrete Optimal Search Library (DOSL) is a fork of the Yet Another Graph-Search Based Planning Library (YAGSBPL) hosted at https://github.com/subh83/YAGSBPL . YAGSBPL is now deprecated.


Installation and Compilation of Examples:

Installation (optional): DOSL is (to a large extent) template-based. There is nothing to build for the library itself. Simply include the header "dosl/dosl" in your C++ code to use DOSL. You can (optionally) install the headers in the system folder by running

 sudo make install

Compilation: Quick compilation of all the examples in the examples-dosl folder (this will ask you to choose an algorithm):

 cd examples-dosl
make all

Alternatively, run make simple or make advanced to compile a selection of the example programs only (see examples-dosl/makefile for other make rules). All executables are created in the examples-dosl/bin folder.

Running the Examples: After compilation, to run an example program:

 ./bin/<program_name>

Or to run all compiled example programs under the ./bin folder:

 make run

Basic Usage:

DOSL uses graphs as the most basic form of discrete representation. In order to describe a graph to DOSL, the user needs to define two classes:

  1. A class defining the type of node/vertex in the graph. This class needs to be derived from DOSL's [AlgName]::Node class (where[AlgName] is the name of the algorithm being used). The user must overload operator== for this class so that DOSL knows how to tell whether two nodes are the same or different.

  2. A class defining the other aspects of the graph structure and the search problem. This class needs to be derived from DOSL's [AlgName]::Algorithm class. Typical members of this class (virtual members of [AlgName]::Algorithm) that the user needs to define are getSuccessors, getStartNodes, stopSearch and getHeuristics.

Below is a bare-bones example, with explanations in comments, illustrating the use of DOSL with the AStar algorithm:

// standard headers
#include<stdio.h>
#include<math.h>
#include<vector>
#include<iostream>// DOSL headers:
#include<dosl/dosl>// ==============================================================================/* The following class defines the type of a vertex of the graph. Needs to be derived from DOSL-provided class template 'AStar::Node<node_type,cost_type>' */classmyNode : publicAStar::Node<myNode,double>
{
public:int x, y; // (x,y) coordinates defining a point on plane.myNode () { }
myNode (int xx, int yy) : x(xx), y(yy) { }
// The comparison operator must be defined for the node type.booloperator==(const myNode& n) const { return (x==n.x && y==n.y); }
// An efficint hash function, 'getHashBin', for node type is desired, but is optional.intgetHashBin (void) { return (abs(((int)x>>4) + ((int)y<<3) + ((int)x<<4) + ((int)y>>3))); }
// optional printing function, 'print':voidprint (std::string head="", std::string tail="") const
{ _dosl_cout << head << "x=" << x << ", y=" << y << _dosl_endl; }
};
// ==============================================================================/* The following class contains the description of the graph (graph connectivity) and search problem description (start and stop criteria). Needs to be derived from DOSL-provided class template 'AStar::Algorithm<search_problem_class,node_type,cost_type>' */classsearchProblem : publicAStar::Algorithm<searchProblem,myNode,double>
{
public:// user-defined problem parameters:
myNode goal_node;
// Constructors, if anysearchProblem () { goal_node = myNode(150,100); }
// -----------------------------------------------------------/* The following functions are use by the base class 'AStar::Algorithm' to determine  graph structure and search parameters. *//* Prototype for 'AStar::Algorithm<>::getSuccessors': void getSuccessors  (NodeType& n, std::vector<NodeType>* const s, std::vector<CostType>* const s); Description: Takes in a vertex, n, and returns its neighbors/successors, s,  and the costs/distances of the edges, c. This defines graph connectivity. */voidgetSuccessors (myNode &n, std::vector<myNode>* s, std::vector<double>* c) {
// This function should account for obstacles, constraints and size of environment.
myNode tn;
for (int a=-1; a<=1; a++)
for (int b=-1; b<=1; b++) {
if (a==0 && b==0) continue;
tn.x = n.x + a;
tn.y = n.y + b;
s->push_back(tn);
c->push_back(sqrt((double)(a*a+b*b))); }
}
/* Prototype for 'AStar::Algorithm<>::getStartNodes': std::vector<NodeType> getStartNodes (void); Description: Should return the list of vertices(s) to start the search with. */
std::vector<myNode> getStartNodes (void) {
std::vector<myNode> startNodes;
for (int a=0; a<1; ++a) {
myNode tn (0, 0); // start node
startNodes.push_back (tn);
}
return (startNodes);
}
// Optional Functions:/* Prototype for 'AStar::Algorithm<>::stopSearch': bool stopSearch (NodeType& n); Description: Determines whether to stop the search when 'n' is being expanded. Optional -- If not provided, search will terminate only when heap is empty. */boolstopSearch (myNode &n) {
return (n==goal_node);
}
/* Prototype for 'AStar::Algorithm<>::getHeuristics': CostType getHeuristics (NodeType& n); Description: Heuristic function for the search. Optional -- If not provided, a zero heuristic is assumed. */doublegetHeuristics (myNode& n) {
double dx = goal_node.x - n.x;
double dy = goal_node.y - n.y;
return (sqrt(dx*dx + dy*dy)); // Euclidean heuristic function
}
};
// ==============================================================================intmain(int argc, char *argv[])
{
searchProblem test_search_problem; // declare an instance of the search problem.
test_search_problem.search(); // execute search.// get path from start to goal vertex.
std::vector<myNode*> path = test_search_problem.reconstruct_pointer_path (test_search_problem.goal_node);
// print pathprintf ("\nPath: \n[");
for (int a=path.size()-1; a>=0; --a) {
std::cout << "[" << path[a]->x << ", " << path[a]->y << "]";
if (a>0) std::cout << "; ";
else std::cout << "]\n\n";
}
return (1);
}

Encapsulations:

For common types of graph, encaptulations can be used to hide some of the details involved in defining the graph type. Currently there are two encaptsulations that are provided with DOSL:

Single shortest path planning in an OpenCV image:

Include dosl/encapsulations/cvPathPlanner.tcc in your code. Then you can compute the optimal path from a start pixel to a goal pixel in a OpenCV image obs_map by creating a variable of type cvPathPlanner<[AlgName]> (where [AlgName] is the name of the search algorithm) using the constructor

cvPathPlanner::cvPathPlanner (cv::Mat obs_map, ...);

and then calling the member

voidcvPathPlanner::find_path (cv::Point start, cv::Point goal, bool vis=false);

Set vis to true to visualize the search. The shortest path is stored in the member std::vector<cv::Point> path. You can directly draw the path in a matrix using either of the following functions:

voidcvPathPlanner::draw_path (cv::Mat& in_map, cv::Mat& out_map, ...);
cv::Mat cvPathPlanner::draw_path (...);

See the example in examples-dosl/src/simple/map2d_encapsulated_PathPlanning.cpp for more details.

Multiple shortest paths in different topological classes in an OpenCV image:

Include dosl/encapsulations/cvMulticlassPathPlanner.tcc in your code. Then create an instance of cvMulticlassPathPlanner<[AlgName]> (where [AlgName] is the name of the search algorithm) using the constructor

cvMulticlassPathPlanner::cvMulticlassPathPlanner (cv::Mat obs_map, ...)

and compute the paths using member

voidcvMulticlassPathPlanner::find_paths (cv::Point start, cv::Point goal, int nPaths=1, bool vis=false, int obsSizeThresh=0);

obsSizeThresh is the minimum size of obstacle that would create multiple classes of paths. The shortest path is stored in the member std::vector< std::vector< cv::Point > > paths. You can directly draw the path in a matrix using either of the following functions:

voidcvMulticlassPathPlanner::draw_paths (cv::Mat& in_map, cv::Mat& out_map, ...);
cv::Mat cvMulticlassPathPlanner::draw_paths (...)

See the example in examples-dosl/src/simple/map2d_encapsulated_HomotopyPathPlanning.cpp for more details.


Documentation:

DOSL wiki is under construction: https://github.com/subh83/DOSL/wiki


Citation:

If you found this library useful in your research, please cite it in your paper as follows:

Suggested citation format: Subhrajit Bhattacharya, "Discrete Optimal Search Library (DOSL): A template-based C++ library for discrete optimal search", 2017. Available at https://github.com/subh83/DOSL .

Bibtex entry:

 @misc{dosl,
title = {Discrete Optimal Search Library (DOSL): A template-based C++ library for discrete optimal search},
author = {Subhrajit Bhattacharya},
note = {Available at https://github.com/subh83/DOSL },
url = { https://github.com/subh83/DOSL },
year = {2017}
}

If you, in particular, use the S-star search algorithm, please use the following citation:

Subhrajit Bhattacharya, "Towards optimal path computation in a simplicial complex", The International Journal of Robotics Research (IJRR), online-first, June, 2019. DOI: 10.1177/0278364919855422.

Bibtex entry:

@ARTICLE { Simplicial:star:19,
AUTHOR = { Subhrajit Bhattacharya },
TITLE = { Towards optimal path computation in a simplicial complex },
JOURNAL = { The International Journal of Robotics Research (IJRR) },
MONTH = { June },
YEAR = { 2019 },
NOTE = { DOI: 10.1177/0278364919855422 },
URL = { https://doi.org/10.1177/0278364919855422 }
}

Version history:

  • Jan 2023: version 3.32: Graph directionality auto-correct bug-fix in SStar; Added demonstration of use of non-zero heuristic function in example program and the bare-bones example.

  • May 2019: version 3.31: 'multiple definition' bug fix.

  • May 2019: version 3.3 released: virtual functions replaced by CRTP in [AlgName]::Algorithm classes, thus making some programs run twice as fast; replaced node and simplex containers/heaps with pointers, making way for multi-thread programming in future; renaming of some variables and functions (back compatibility included); bug fixes.

  • Nov 2018: version 3.26 released: Some bug fixes. Changes made to encapsulation member functions.

  • Sep 2017: version 3.25 released: Added ThetaStar and SStar search algorithms; Organized components of algorithm under nested classes; More extensive and organized examples grouped into "simple" and "advanced"; Simple encapsulations for path planning in OpenCV matrices added.

  • May 2017: version 3.1 released

  • Nov 2016: version 3.0a released

  • Discrete Optimal Search Library (DOSL) is a fork of the Yet Another Graph-Search Based Planning Library (YAGSBPL) hosted at https://github.com/subh83/YAGSBPL . YAGSBPL is now deprecated.


License:

/** **************************************************************************************
* *
* Part of *
* Discrete Optimal Search Library (DOSL) *
* A template-based C++ library for discrete search *
* Version 3.x *
* ---------------------------------------------------------- *
* Copyright (C) 2017 Subhrajit Bhattacharya *
* *
* This program 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 3 of the License, or *
* (at your option) any later version. *
* *
* This program 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 <http://www.gnu.org/licenses/>. *
* *
* *
* Contact: subhrajit@gmail.com *
* https://www.lehigh.edu/~sub216/ , http://subhrajit.net/ *
* *
* *
*************************************************************************************** **/

About

Discrete Optimal Search Library (DOSL): A template-based C++ library for searching, path-finding and exploring discrete spaces such as graphs and simplicial complexes

Resources

Stars

50 stars

Watchers

5 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Latest commit

History

65 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Discrete Optimal Search Library (DOSL)

subh83/DOSL

Description:

"Discrete Optimal Search Library (DOSL)" is a fast, efficient and easy-to-use library for construction of discrete representation (e.g., a graph or a simplicial complex) and search (e.g., using algorithms like A-star, Dijkstra's, etc.) library written in C++, designed specifically for searching medium to large scale graphs or simplicial complexes for optimal paths.


Output from example program map2d_PathPlanning showing the progress of A* search algorithm in finding shortest path in an 8-connected grid graph and using an Euclidean heuristic function (requires OpenCV):

5000 vertices expanded10000 vertices expanded15000 vertices expandedFinal path

See the Overview of Selected Example Programs for more sample outputs from the provided example programs.


DOSL is designed to be:

  • Fast (e.g., with integer coordinates for nodes but floating point cost as well as cost function needing to perform floating point operations online, and an average degree of the graph being 8, the A* search algorithm of the library can expand about 150,000 nodes in the graph in about 0.53 second on a 1.8GHz quad-core processor machine with 16GB RAM.)
  • Easy to use / versatile (being template-based, defining new arbitrary node-types, cost types, etc. is made easy. For graph connectivity, node accessibility tests, etc, user-defined classes can be used, which makes defining the graph structure very easy, yet highly flexible.)

DOSL supports:

  • Directed graphs with complex cost functions.
  • On the fly graph construction (i.e. no need to construct and store a complete graph before starting the search/planning process - makes it highly suitable for RRT-like graph construction).
  • Arbitrary graph node type declaration.
  • Arbitrary edge cost type declaration.
  • Intermediate storage of paths during graph search.
  • Multiple goals (or goal manifold) that determine when to stop search.
  • Multiple start nodes for wave-front expansion type graph exploration.
  • Event handling (i.e., call to user-defined functions upon generation, g-score updating or expansion of a node during the search process).
  • Other forms of discrete representations, such as simplicial complexes (S-star algorithm), is supported by specific planners.
  • Ability to write new planners with ease. Comes with a weighted A-star (that includes Dijkstra's and normal A-star), Theta-star and S-star planner by default.

New:

  • The [AlgName]::Algorithm class template now takes in the derived class as its first template parameter (a CRTP). Thus, definition of a searchProblem class should now be made as follows:
classsearchProblem : publicAStar::Algorithm <searchProblem, myNode, double>
{ /* ... */}; // ^^^^ new template parameter

This change (replacing virtual functions with CRTP) has helped some example programs to run twice as fast.
Backward compatibility note (since v3.3): Users need to make this change for backward compatibility.
See the example programs for illustration and details.

NOTE: Discrete Optimal Search Library (DOSL) is a fork of the Yet Another Graph-Search Based Planning Library (YAGSBPL) hosted at https://github.com/subh83/YAGSBPL . YAGSBPL is now deprecated.


Installation and Compilation of Examples:

Installation (optional): DOSL is (to a large extent) template-based. There is nothing to build for the library itself. Simply include the header "dosl/dosl" in your C++ code to use DOSL. You can (optionally) install the headers in the system folder by running

 sudo make install

Compilation: Quick compilation of all the examples in the examples-dosl folder (this will ask you to choose an algorithm):

 cd examples-dosl
make all

Alternatively, run make simple or make advanced to compile a selection of the example programs only (see examples-dosl/makefile for other make rules). All executables are created in the examples-dosl/bin folder.

Running the Examples: After compilation, to run an example program:

 ./bin/<program_name>

Or to run all compiled example programs under the ./bin folder:

 make run

Basic Usage:

DOSL uses graphs as the most basic form of discrete representation. In order to describe a graph to DOSL, the user needs to define two classes:

  1. A class defining the type of node/vertex in the graph. This class needs to be derived from DOSL's [AlgName]::Node class (where[AlgName] is the name of the algorithm being used). The user must overload operator== for this class so that DOSL knows how to tell whether two nodes are the same or different.

  2. A class defining the other aspects of the graph structure and the search problem. This class needs to be derived from DOSL's [AlgName]::Algorithm class. Typical members of this class (virtual members of [AlgName]::Algorithm) that the user needs to define are getSuccessors, getStartNodes, stopSearch and getHeuristics.

Below is a bare-bones example, with explanations in comments, illustrating the use of DOSL with the AStar algorithm:

// standard headers
#include<stdio.h>
#include<math.h>
#include<vector>
#include<iostream>// DOSL headers:
#include<dosl/dosl>// ==============================================================================/* The following class defines the type of a vertex of the graph. Needs to be derived from DOSL-provided class template 'AStar::Node<node_type,cost_type>' */classmyNode : publicAStar::Node<myNode,double>
{
public:int x, y; // (x,y) coordinates defining a point on plane.myNode () { }
myNode (int xx, int yy) : x(xx), y(yy) { }
// The comparison operator must be defined for the node type.booloperator==(const myNode& n) const { return (x==n.x && y==n.y); }
// An efficint hash function, 'getHashBin', for node type is desired, but is optional.intgetHashBin (void) { return (abs(((int)x>>4) + ((int)y<<3) + ((int)x<<4) + ((int)y>>3))); }
// optional printing function, 'print':voidprint (std::string head="", std::string tail="") const
{ _dosl_cout << head << "x=" << x << ", y=" << y << _dosl_endl; }
};
// ==============================================================================/* The following class contains the description of the graph (graph connectivity) and search problem description (start and stop criteria). Needs to be derived from DOSL-provided class template 'AStar::Algorithm<search_problem_class,node_type,cost_type>' */classsearchProblem : publicAStar::Algorithm<searchProblem,myNode,double>
{
public:// user-defined problem parameters:
myNode goal_node;
// Constructors, if anysearchProblem () { goal_node = myNode(150,100); }
// -----------------------------------------------------------/* The following functions are use by the base class 'AStar::Algorithm' to determine  graph structure and search parameters. *//* Prototype for 'AStar::Algorithm<>::getSuccessors': void getSuccessors  (NodeType& n, std::vector<NodeType>* const s, std::vector<CostType>* const s); Description: Takes in a vertex, n, and returns its neighbors/successors, s,  and the costs/distances of the edges, c. This defines graph connectivity. */voidgetSuccessors (myNode &n, std::vector<myNode>* s, std::vector<double>* c) {
// This function should account for obstacles, constraints and size of environment.
myNode tn;
for (int a=-1; a<=1; a++)
for (int b=-1; b<=1; b++) {
if (a==0 && b==0) continue;
tn.x = n.x + a;
tn.y = n.y + b;
s->push_back(tn);
c->push_back(sqrt((double)(a*a+b*b))); }
}
/* Prototype for 'AStar::Algorithm<>::getStartNodes': std::vector<NodeType> getStartNodes (void); Description: Should return the list of vertices(s) to start the search with. */
std::vector<myNode> getStartNodes (void) {
std::vector<myNode> startNodes;
for (int a=0; a<1; ++a) {
myNode tn (0, 0); // start node
startNodes.push_back (tn);
}
return (startNodes);
}
// Optional Functions:/* Prototype for 'AStar::Algorithm<>::stopSearch': bool stopSearch (NodeType& n); Description: Determines whether to stop the search when 'n' is being expanded. Optional -- If not provided, search will terminate only when heap is empty. */boolstopSearch (myNode &n) {
return (n==goal_node);
}
/* Prototype for 'AStar::Algorithm<>::getHeuristics': CostType getHeuristics (NodeType& n); Description: Heuristic function for the search. Optional -- If not provided, a zero heuristic is assumed. */doublegetHeuristics (myNode& n) {
double dx = goal_node.x - n.x;
double dy = goal_node.y - n.y;
return (sqrt(dx*dx + dy*dy)); // Euclidean heuristic function
}
};
// ==============================================================================intmain(int argc, char *argv[])
{
searchProblem test_search_problem; // declare an instance of the search problem.
test_search_problem.search(); // execute search.// get path from start to goal vertex.
std::vector<myNode*> path = test_search_problem.reconstruct_pointer_path (test_search_problem.goal_node);
// print pathprintf ("\nPath: \n[");
for (int a=path.size()-1; a>=0; --a) {
std::cout << "[" << path[a]->x << ", " << path[a]->y << "]";
if (a>0) std::cout << "; ";
else std::cout << "]\n\n";
}
return (1);
}

Encapsulations:

For common types of graph, encaptulations can be used to hide some of the details involved in defining the graph type. Currently there are two encaptsulations that are provided with DOSL:

Single shortest path planning in an OpenCV image:

Include dosl/encapsulations/cvPathPlanner.tcc in your code. Then you can compute the optimal path from a start pixel to a goal pixel in a OpenCV image obs_map by creating a variable of type cvPathPlanner<[AlgName]> (where [AlgName] is the name of the search algorithm) using the constructor

cvPathPlanner::cvPathPlanner (cv::Mat obs_map, ...);

and then calling the member

voidcvPathPlanner::find_path (cv::Point start, cv::Point goal, bool vis=false);

Set vis to true to visualize the search. The shortest path is stored in the member std::vector<cv::Point> path. You can directly draw the path in a matrix using either of the following functions:

voidcvPathPlanner::draw_path (cv::Mat& in_map, cv::Mat& out_map, ...);
cv::Mat cvPathPlanner::draw_path (...);

See the example in examples-dosl/src/simple/map2d_encapsulated_PathPlanning.cpp for more details.

Multiple shortest paths in different topological classes in an OpenCV image:

Include dosl/encapsulations/cvMulticlassPathPlanner.tcc in your code. Then create an instance of cvMulticlassPathPlanner<[AlgName]> (where [AlgName] is the name of the search algorithm) using the constructor

cvMulticlassPathPlanner::cvMulticlassPathPlanner (cv::Mat obs_map, ...)

and compute the paths using member

voidcvMulticlassPathPlanner::find_paths (cv::Point start, cv::Point goal, int nPaths=1, bool vis=false, int obsSizeThresh=0);

obsSizeThresh is the minimum size of obstacle that would create multiple classes of paths. The shortest path is stored in the member std::vector< std::vector< cv::Point > > paths. You can directly draw the path in a matrix using either of the following functions:

voidcvMulticlassPathPlanner::draw_paths (cv::Mat& in_map, cv::Mat& out_map, ...);
cv::Mat cvMulticlassPathPlanner::draw_paths (...)

See the example in examples-dosl/src/simple/map2d_encapsulated_HomotopyPathPlanning.cpp for more details.


Documentation:

DOSL wiki is under construction: https://github.com/subh83/DOSL/wiki


Citation:

If you found this library useful in your research, please cite it in your paper as follows:

Suggested citation format: Subhrajit Bhattacharya, "Discrete Optimal Search Library (DOSL): A template-based C++ library for discrete optimal search", 2017. Available at https://github.com/subh83/DOSL .

Bibtex entry:

 @misc{dosl,
title = {Discrete Optimal Search Library (DOSL): A template-based C++ library for discrete optimal search},
author = {Subhrajit Bhattacharya},
note = {Available at https://github.com/subh83/DOSL },
url = { https://github.com/subh83/DOSL },
year = {2017}
}

If you, in particular, use the S-star search algorithm, please use the following citation:

Subhrajit Bhattacharya, "Towards optimal path computation in a simplicial complex", The International Journal of Robotics Research (IJRR), online-first, June, 2019. DOI: 10.1177/0278364919855422.

Bibtex entry:

@ARTICLE { Simplicial:star:19,
AUTHOR = { Subhrajit Bhattacharya },
TITLE = { Towards optimal path computation in a simplicial complex },
JOURNAL = { The International Journal of Robotics Research (IJRR) },
MONTH = { June },
YEAR = { 2019 },
NOTE = { DOI: 10.1177/0278364919855422 },
URL = { https://doi.org/10.1177/0278364919855422 }
}

Version history:

  • Jan 2023: version 3.32: Graph directionality auto-correct bug-fix in SStar; Added demonstration of use of non-zero heuristic function in example program and the bare-bones example.

  • May 2019: version 3.31: 'multiple definition' bug fix.

  • May 2019: version 3.3 released: virtual functions replaced by CRTP in [AlgName]::Algorithm classes, thus making some programs run twice as fast; replaced node and simplex containers/heaps with pointers, making way for multi-thread programming in future; renaming of some variables and functions (back compatibility included); bug fixes.

  • Nov 2018: version 3.26 released: Some bug fixes. Changes made to encapsulation member functions.

  • Sep 2017: version 3.25 released: Added ThetaStar and SStar search algorithms; Organized components of algorithm under nested classes; More extensive and organized examples grouped into "simple" and "advanced"; Simple encapsulations for path planning in OpenCV matrices added.

  • May 2017: version 3.1 released

  • Nov 2016: version 3.0a released

  • Discrete Optimal Search Library (DOSL) is a fork of the Yet Another Graph-Search Based Planning Library (YAGSBPL) hosted at https://github.com/subh83/YAGSBPL . YAGSBPL is now deprecated.


License:

/** **************************************************************************************
* *
* Part of *
* Discrete Optimal Search Library (DOSL) *
* A template-based C++ library for discrete search *
* Version 3.x *
* ---------------------------------------------------------- *
* Copyright (C) 2017 Subhrajit Bhattacharya *
* *
* This program 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 3 of the License, or *
* (at your option) any later version. *
* *
* This program 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 <http://www.gnu.org/licenses/>. *
* *
* *
* Contact: subhrajit@gmail.com *
* https://www.lehigh.edu/~sub216/ , http://subhrajit.net/ *
* *
* *
*************************************************************************************** **/

About

Discrete Optimal Search Library (DOSL): A template-based C++ library for searching, path-finding and exploring discrete spaces such as graphs and simplicial complexes

Resources

Stars

50 stars

Watchers

5 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

65 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Discrete Optimal Search Library (DOSL)

subh83/DOSL

Description:

"Discrete Optimal Search Library (DOSL)" is a fast, efficient and easy-to-use library for construction of discrete representation (e.g., a graph or a simplicial complex) and search (e.g., using algorithms like A-star, Dijkstra's, etc.) library written in C++, designed specifically for searching medium to large scale graphs or simplicial complexes for optimal paths.


Output from example program map2d_PathPlanning showing the progress of A* search algorithm in finding shortest path in an 8-connected grid graph and using an Euclidean heuristic function (requires OpenCV):

5000 vertices expanded10000 vertices expanded15000 vertices expandedFinal path

See the Overview of Selected Example Programs for more sample outputs from the provided example programs.


DOSL is designed to be:

  • Fast (e.g., with integer coordinates for nodes but floating point cost as well as cost function needing to perform floating point operations online, and an average degree of the graph being 8, the A* search algorithm of the library can expand about 150,000 nodes in the graph in about 0.53 second on a 1.8GHz quad-core processor machine with 16GB RAM.)
  • Easy to use / versatile (being template-based, defining new arbitrary node-types, cost types, etc. is made easy. For graph connectivity, node accessibility tests, etc, user-defined classes can be used, which makes defining the graph structure very easy, yet highly flexible.)

DOSL supports:

  • Directed graphs with complex cost functions.
  • On the fly graph construction (i.e. no need to construct and store a complete graph before starting the search/planning process - makes it highly suitable for RRT-like graph construction).
  • Arbitrary graph node type declaration.
  • Arbitrary edge cost type declaration.
  • Intermediate storage of paths during graph search.
  • Multiple goals (or goal manifold) that determine when to stop search.
  • Multiple start nodes for wave-front expansion type graph exploration.
  • Event handling (i.e., call to user-defined functions upon generation, g-score updating or expansion of a node during the search process).
  • Other forms of discrete representations, such as simplicial complexes (S-star algorithm), is supported by specific planners.
  • Ability to write new planners with ease. Comes with a weighted A-star (that includes Dijkstra's and normal A-star), Theta-star and S-star planner by default.

New:

  • The [AlgName]::Algorithm class template now takes in the derived class as its first template parameter (a CRTP). Thus, definition of a searchProblem class should now be made as follows:
classsearchProblem : publicAStar::Algorithm <searchProblem, myNode, double>
{ /* ... */}; // ^^^^ new template parameter

This change (replacing virtual functions with CRTP) has helped some example programs to run twice as fast.
Backward compatibility note (since v3.3): Users need to make this change for backward compatibility.
See the example programs for illustration and details.

NOTE: Discrete Optimal Search Library (DOSL) is a fork of the Yet Another Graph-Search Based Planning Library (YAGSBPL) hosted at https://github.com/subh83/YAGSBPL . YAGSBPL is now deprecated.


Installation and Compilation of Examples:

Installation (optional): DOSL is (to a large extent) template-based. There is nothing to build for the library itself. Simply include the header "dosl/dosl" in your C++ code to use DOSL. You can (optionally) install the headers in the system folder by running

 sudo make install

Compilation: Quick compilation of all the examples in the examples-dosl folder (this will ask you to choose an algorithm):

 cd examples-dosl
make all

Alternatively, run make simple or make advanced to compile a selection of the example programs only (see examples-dosl/makefile for other make rules). All executables are created in the examples-dosl/bin folder.

Running the Examples: After compilation, to run an example program:

 ./bin/<program_name>

Or to run all compiled example programs under the ./bin folder:

 make run

Basic Usage:

DOSL uses graphs as the most basic form of discrete representation. In order to describe a graph to DOSL, the user needs to define two classes:

  1. A class defining the type of node/vertex in the graph. This class needs to be derived from DOSL's [AlgName]::Node class (where[AlgName] is the name of the algorithm being used). The user must overload operator== for this class so that DOSL knows how to tell whether two nodes are the same or different.

  2. A class defining the other aspects of the graph structure and the search problem. This class needs to be derived from DOSL's [AlgName]::Algorithm class. Typical members of this class (virtual members of [AlgName]::Algorithm) that the user needs to define are getSuccessors, getStartNodes, stopSearch and getHeuristics.

Below is a bare-bones example, with explanations in comments, illustrating the use of DOSL with the AStar algorithm:

// standard headers
#include<stdio.h>
#include<math.h>
#include<vector>
#include<iostream>// DOSL headers:
#include<dosl/dosl>// ==============================================================================/* The following class defines the type of a vertex of the graph. Needs to be derived from DOSL-provided class template 'AStar::Node<node_type,cost_type>' */classmyNode : publicAStar::Node<myNode,double>
{
public:int x, y; // (x,y) coordinates defining a point on plane.myNode () { }
myNode (int xx, int yy) : x(xx), y(yy) { }
// The comparison operator must be defined for the node type.booloperator==(const myNode& n) const { return (x==n.x && y==n.y); }
// An efficint hash function, 'getHashBin', for node type is desired, but is optional.intgetHashBin (void) { return (abs(((int)x>>4) + ((int)y<<3) + ((int)x<<4) + ((int)y>>3))); }
// optional printing function, 'print':voidprint (std::string head="", std::string tail="") const
{ _dosl_cout << head << "x=" << x << ", y=" << y << _dosl_endl; }
};
// ==============================================================================/* The following class contains the description of the graph (graph connectivity) and search problem description (start and stop criteria). Needs to be derived from DOSL-provided class template 'AStar::Algorithm<search_problem_class,node_type,cost_type>' */classsearchProblem : publicAStar::Algorithm<searchProblem,myNode,double>
{
public:// user-defined problem parameters:
myNode goal_node;
// Constructors, if anysearchProblem () { goal_node = myNode(150,100); }
// -----------------------------------------------------------/* The following functions are use by the base class 'AStar::Algorithm' to determine  graph structure and search parameters. *//* Prototype for 'AStar::Algorithm<>::getSuccessors': void getSuccessors  (NodeType& n, std::vector<NodeType>* const s, std::vector<CostType>* const s); Description: Takes in a vertex, n, and returns its neighbors/successors, s,  and the costs/distances of the edges, c. This defines graph connectivity. */voidgetSuccessors (myNode &n, std::vector<myNode>* s, std::vector<double>* c) {
// This function should account for obstacles, constraints and size of environment.
myNode tn;
for (int a=-1; a<=1; a++)
for (int b=-1; b<=1; b++) {
if (a==0 && b==0) continue;
tn.x = n.x + a;
tn.y = n.y + b;
s->push_back(tn);
c->push_back(sqrt((double)(a*a+b*b))); }
}
/* Prototype for 'AStar::Algorithm<>::getStartNodes': std::vector<NodeType> getStartNodes (void); Description: Should return the list of vertices(s) to start the search with. */
std::vector<myNode> getStartNodes (void) {
std::vector<myNode> startNodes;
for (int a=0; a<1; ++a) {
myNode tn (0, 0); // start node
startNodes.push_back (tn);
}
return (startNodes);
}
// Optional Functions:/* Prototype for 'AStar::Algorithm<>::stopSearch': bool stopSearch (NodeType& n); Description: Determines whether to stop the search when 'n' is being expanded. Optional -- If not provided, search will terminate only when heap is empty. */boolstopSearch (myNode &n) {
return (n==goal_node);
}
/* Prototype for 'AStar::Algorithm<>::getHeuristics': CostType getHeuristics (NodeType& n); Description: Heuristic function for the search. Optional -- If not provided, a zero heuristic is assumed. */doublegetHeuristics (myNode& n) {
double dx = goal_node.x - n.x;
double dy = goal_node.y - n.y;
return (sqrt(dx*dx + dy*dy)); // Euclidean heuristic function
}
};
// ==============================================================================intmain(int argc, char *argv[])
{
searchProblem test_search_problem; // declare an instance of the search problem.
test_search_problem.search(); // execute search.// get path from start to goal vertex.
std::vector<myNode*> path = test_search_problem.reconstruct_pointer_path (test_search_problem.goal_node);
// print pathprintf ("\nPath: \n[");
for (int a=path.size()-1; a>=0; --a) {
std::cout << "[" << path[a]->x << ", " << path[a]->y << "]";
if (a>0) std::cout << "; ";
else std::cout << "]\n\n";
}
return (1);
}

Encapsulations:

For common types of graph, encaptulations can be used to hide some of the details involved in defining the graph type. Currently there are two encaptsulations that are provided with DOSL:

Single shortest path planning in an OpenCV image:

Include dosl/encapsulations/cvPathPlanner.tcc in your code. Then you can compute the optimal path from a start pixel to a goal pixel in a OpenCV image obs_map by creating a variable of type cvPathPlanner<[AlgName]> (where [AlgName] is the name of the search algorithm) using the constructor

cvPathPlanner::cvPathPlanner (cv::Mat obs_map, ...);

and then calling the member

voidcvPathPlanner::find_path (cv::Point start, cv::Point goal, bool vis=false);

Set vis to true to visualize the search. The shortest path is stored in the member std::vector<cv::Point> path. You can directly draw the path in a matrix using either of the following functions:

voidcvPathPlanner::draw_path (cv::Mat& in_map, cv::Mat& out_map, ...);
cv::Mat cvPathPlanner::draw_path (...);

See the example in examples-dosl/src/simple/map2d_encapsulated_PathPlanning.cpp for more details.

Multiple shortest paths in different topological classes in an OpenCV image:

Include dosl/encapsulations/cvMulticlassPathPlanner.tcc in your code. Then create an instance of cvMulticlassPathPlanner<[AlgName]> (where [AlgName] is the name of the search algorithm) using the constructor

cvMulticlassPathPlanner::cvMulticlassPathPlanner (cv::Mat obs_map, ...)

and compute the paths using member

voidcvMulticlassPathPlanner::find_paths (cv::Point start, cv::Point goal, int nPaths=1, bool vis=false, int obsSizeThresh=0);

obsSizeThresh is the minimum size of obstacle that would create multiple classes of paths. The shortest path is stored in the member std::vector< std::vector< cv::Point > > paths. You can directly draw the path in a matrix using either of the following functions:

voidcvMulticlassPathPlanner::draw_paths (cv::Mat& in_map, cv::Mat& out_map, ...);
cv::Mat cvMulticlassPathPlanner::draw_paths (...)

See the example in examples-dosl/src/simple/map2d_encapsulated_HomotopyPathPlanning.cpp for more details.


Documentation:

DOSL wiki is under construction: https://github.com/subh83/DOSL/wiki


Citation:

If you found this library useful in your research, please cite it in your paper as follows:

Suggested citation format: Subhrajit Bhattacharya, "Discrete Optimal Search Library (DOSL): A template-based C++ library for discrete optimal search", 2017. Available at https://github.com/subh83/DOSL .

Bibtex entry:

 @misc{dosl,
title = {Discrete Optimal Search Library (DOSL): A template-based C++ library for discrete optimal search},
author = {Subhrajit Bhattacharya},
note = {Available at https://github.com/subh83/DOSL },
url = { https://github.com/subh83/DOSL },
year = {2017}
}

If you, in particular, use the S-star search algorithm, please use the following citation:

Subhrajit Bhattacharya, "Towards optimal path computation in a simplicial complex", The International Journal of Robotics Research (IJRR), online-first, June, 2019. DOI: 10.1177/0278364919855422.

Bibtex entry:

@ARTICLE { Simplicial:star:19,
AUTHOR = { Subhrajit Bhattacharya },
TITLE = { Towards optimal path computation in a simplicial complex },
JOURNAL = { The International Journal of Robotics Research (IJRR) },
MONTH = { June },
YEAR = { 2019 },
NOTE = { DOI: 10.1177/0278364919855422 },
URL = { https://doi.org/10.1177/0278364919855422 }
}

Version history:

  • Jan 2023: version 3.32: Graph directionality auto-correct bug-fix in SStar; Added demonstration of use of non-zero heuristic function in example program and the bare-bones example.

  • May 2019: version 3.31: 'multiple definition' bug fix.

  • May 2019: version 3.3 released: virtual functions replaced by CRTP in [AlgName]::Algorithm classes, thus making some programs run twice as fast; replaced node and simplex containers/heaps with pointers, making way for multi-thread programming in future; renaming of some variables and functions (back compatibility included); bug fixes.

  • Nov 2018: version 3.26 released: Some bug fixes. Changes made to encapsulation member functions.

  • Sep 2017: version 3.25 released: Added ThetaStar and SStar search algorithms; Organized components of algorithm under nested classes; More extensive and organized examples grouped into "simple" and "advanced"; Simple encapsulations for path planning in OpenCV matrices added.

  • May 2017: version 3.1 released

  • Nov 2016: version 3.0a released

  • Discrete Optimal Search Library (DOSL) is a fork of the Yet Another Graph-Search Based Planning Library (YAGSBPL) hosted at https://github.com/subh83/YAGSBPL . YAGSBPL is now deprecated.


License:

/** **************************************************************************************
* *
* Part of *
* Discrete Optimal Search Library (DOSL) *
* A template-based C++ library for discrete search *
* Version 3.x *
* ---------------------------------------------------------- *
* Copyright (C) 2017 Subhrajit Bhattacharya *
* *
* This program 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 3 of the License, or *
* (at your option) any later version. *
* *
* This program 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 <http://www.gnu.org/licenses/>. *
* *
* *
* Contact: subhrajit@gmail.com *
* https://www.lehigh.edu/~sub216/ , http://subhrajit.net/ *
* *
* *
*************************************************************************************** **/

About

Discrete Optimal Search Library (DOSL): A template-based C++ library for searching, path-finding and exploring discrete spaces such as graphs and simplicial complexes

Resources

Stars

50 stars

Watchers

5 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

65 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Discrete Optimal Search Library (DOSL)

subh83/DOSL

Description:

"Discrete Optimal Search Library (DOSL)" is a fast, efficient and easy-to-use library for construction of discrete representation (e.g., a graph or a simplicial complex) and search (e.g., using algorithms like A-star, Dijkstra's, etc.) library written in C++, designed specifically for searching medium to large scale graphs or simplicial complexes for optimal paths.


Output from example program map2d_PathPlanning showing the progress of A* search algorithm in finding shortest path in an 8-connected grid graph and using an Euclidean heuristic function (requires OpenCV):

5000 vertices expanded10000 vertices expanded15000 vertices expandedFinal path

See the Overview of Selected Example Programs for more sample outputs from the provided example programs.


DOSL is designed to be:

  • Fast (e.g., with integer coordinates for nodes but floating point cost as well as cost function needing to perform floating point operations online, and an average degree of the graph being 8, the A* search algorithm of the library can expand about 150,000 nodes in the graph in about 0.53 second on a 1.8GHz quad-core processor machine with 16GB RAM.)
  • Easy to use / versatile (being template-based, defining new arbitrary node-types, cost types, etc. is made easy. For graph connectivity, node accessibility tests, etc, user-defined classes can be used, which makes defining the graph structure very easy, yet highly flexible.)

DOSL supports:

  • Directed graphs with complex cost functions.
  • On the fly graph construction (i.e. no need to construct and store a complete graph before starting the search/planning process - makes it highly suitable for RRT-like graph construction).
  • Arbitrary graph node type declaration.
  • Arbitrary edge cost type declaration.
  • Intermediate storage of paths during graph search.
  • Multiple goals (or goal manifold) that determine when to stop search.
  • Multiple start nodes for wave-front expansion type graph exploration.
  • Event handling (i.e., call to user-defined functions upon generation, g-score updating or expansion of a node during the search process).
  • Other forms of discrete representations, such as simplicial complexes (S-star algorithm), is supported by specific planners.
  • Ability to write new planners with ease. Comes with a weighted A-star (that includes Dijkstra's and normal A-star), Theta-star and S-star planner by default.

New:

  • The [AlgName]::Algorithm class template now takes in the derived class as its first template parameter (a CRTP). Thus, definition of a searchProblem class should now be made as follows:
classsearchProblem : publicAStar::Algorithm <searchProblem, myNode, double>
{ /* ... */}; // ^^^^ new template parameter

This change (replacing virtual functions with CRTP) has helped some example programs to run twice as fast.
Backward compatibility note (since v3.3): Users need to make this change for backward compatibility.
See the example programs for illustration and details.

NOTE: Discrete Optimal Search Library (DOSL) is a fork of the Yet Another Graph-Search Based Planning Library (YAGSBPL) hosted at https://github.com/subh83/YAGSBPL . YAGSBPL is now deprecated.


Installation and Compilation of Examples:

Installation (optional): DOSL is (to a large extent) template-based. There is nothing to build for the library itself. Simply include the header "dosl/dosl" in your C++ code to use DOSL. You can (optionally) install the headers in the system folder by running

 sudo make install

Compilation: Quick compilation of all the examples in the examples-dosl folder (this will ask you to choose an algorithm):

 cd examples-dosl
make all

Alternatively, run make simple or make advanced to compile a selection of the example programs only (see examples-dosl/makefile for other make rules). All executables are created in the examples-dosl/bin folder.

Running the Examples: After compilation, to run an example program:

 ./bin/<program_name>

Or to run all compiled example programs under the ./bin folder:

 make run

Basic Usage:

DOSL uses graphs as the most basic form of discrete representation. In order to describe a graph to DOSL, the user needs to define two classes:

  1. A class defining the type of node/vertex in the graph. This class needs to be derived from DOSL's [AlgName]::Node class (where[AlgName] is the name of the algorithm being used). The user must overload operator== for this class so that DOSL knows how to tell whether two nodes are the same or different.

  2. A class defining the other aspects of the graph structure and the search problem. This class needs to be derived from DOSL's [AlgName]::Algorithm class. Typical members of this class (virtual members of [AlgName]::Algorithm) that the user needs to define are getSuccessors, getStartNodes, stopSearch and getHeuristics.

Below is a bare-bones example, with explanations in comments, illustrating the use of DOSL with the AStar algorithm:

// standard headers
#include<stdio.h>
#include<math.h>
#include<vector>
#include<iostream>// DOSL headers:
#include<dosl/dosl>// ==============================================================================/* The following class defines the type of a vertex of the graph. Needs to be derived from DOSL-provided class template 'AStar::Node<node_type,cost_type>' */classmyNode : publicAStar::Node<myNode,double>
{
public:int x, y; // (x,y) coordinates defining a point on plane.myNode () { }
myNode (int xx, int yy) : x(xx), y(yy) { }
// The comparison operator must be defined for the node type.booloperator==(const myNode& n) const { return (x==n.x && y==n.y); }
// An efficint hash function, 'getHashBin', for node type is desired, but is optional.intgetHashBin (void) { return (abs(((int)x>>4) + ((int)y<<3) + ((int)x<<4) + ((int)y>>3))); }
// optional printing function, 'print':voidprint (std::string head="", std::string tail="") const
{ _dosl_cout << head << "x=" << x << ", y=" << y << _dosl_endl; }
};
// ==============================================================================/* The following class contains the description of the graph (graph connectivity) and search problem description (start and stop criteria). Needs to be derived from DOSL-provided class template 'AStar::Algorithm<search_problem_class,node_type,cost_type>' */classsearchProblem : publicAStar::Algorithm<searchProblem,myNode,double>
{
public:// user-defined problem parameters:
myNode goal_node;
// Constructors, if anysearchProblem () { goal_node = myNode(150,100); }
// -----------------------------------------------------------/* The following functions are use by the base class 'AStar::Algorithm' to determine  graph structure and search parameters. *//* Prototype for 'AStar::Algorithm<>::getSuccessors': void getSuccessors  (NodeType& n, std::vector<NodeType>* const s, std::vector<CostType>* const s); Description: Takes in a vertex, n, and returns its neighbors/successors, s,  and the costs/distances of the edges, c. This defines graph connectivity. */voidgetSuccessors (myNode &n, std::vector<myNode>* s, std::vector<double>* c) {
// This function should account for obstacles, constraints and size of environment.
myNode tn;
for (int a=-1; a<=1; a++)
for (int b=-1; b<=1; b++) {
if (a==0 && b==0) continue;
tn.x = n.x + a;
tn.y = n.y + b;
s->push_back(tn);
c->push_back(sqrt((double)(a*a+b*b))); }
}
/* Prototype for 'AStar::Algorithm<>::getStartNodes': std::vector<NodeType> getStartNodes (void); Description: Should return the list of vertices(s) to start the search with. */
std::vector<myNode> getStartNodes (void) {
std::vector<myNode> startNodes;
for (int a=0; a<1; ++a) {
myNode tn (0, 0); // start node
startNodes.push_back (tn);
}
return (startNodes);
}
// Optional Functions:/* Prototype for 'AStar::Algorithm<>::stopSearch': bool stopSearch (NodeType& n); Description: Determines whether to stop the search when 'n' is being expanded. Optional -- If not provided, search will terminate only when heap is empty. */boolstopSearch (myNode &n) {
return (n==goal_node);
}
/* Prototype for 'AStar::Algorithm<>::getHeuristics': CostType getHeuristics (NodeType& n); Description: Heuristic function for the search. Optional -- If not provided, a zero heuristic is assumed. */doublegetHeuristics (myNode& n) {
double dx = goal_node.x - n.x;
double dy = goal_node.y - n.y;
return (sqrt(dx*dx + dy*dy)); // Euclidean heuristic function
}
};
// ==============================================================================intmain(int argc, char *argv[])
{
searchProblem test_search_problem; // declare an instance of the search problem.
test_search_problem.search(); // execute search.// get path from start to goal vertex.
std::vector<myNode*> path = test_search_problem.reconstruct_pointer_path (test_search_problem.goal_node);
// print pathprintf ("\nPath: \n[");
for (int a=path.size()-1; a>=0; --a) {
std::cout << "[" << path[a]->x << ", " << path[a]->y << "]";
if (a>0) std::cout << "; ";
else std::cout << "]\n\n";
}
return (1);
}

Encapsulations:

For common types of graph, encaptulations can be used to hide some of the details involved in defining the graph type. Currently there are two encaptsulations that are provided with DOSL:

Single shortest path planning in an OpenCV image:

Include dosl/encapsulations/cvPathPlanner.tcc in your code. Then you can compute the optimal path from a start pixel to a goal pixel in a OpenCV image obs_map by creating a variable of type cvPathPlanner<[AlgName]> (where [AlgName] is the name of the search algorithm) using the constructor

cvPathPlanner::cvPathPlanner (cv::Mat obs_map, ...);

and then calling the member

voidcvPathPlanner::find_path (cv::Point start, cv::Point goal, bool vis=false);

Set vis to true to visualize the search. The shortest path is stored in the member std::vector<cv::Point> path. You can directly draw the path in a matrix using either of the following functions:

voidcvPathPlanner::draw_path (cv::Mat& in_map, cv::Mat& out_map, ...);
cv::Mat cvPathPlanner::draw_path (...);

See the example in examples-dosl/src/simple/map2d_encapsulated_PathPlanning.cpp for more details.

Multiple shortest paths in different topological classes in an OpenCV image:

Include dosl/encapsulations/cvMulticlassPathPlanner.tcc in your code. Then create an instance of cvMulticlassPathPlanner<[AlgName]> (where [AlgName] is the name of the search algorithm) using the constructor

cvMulticlassPathPlanner::cvMulticlassPathPlanner (cv::Mat obs_map, ...)

and compute the paths using member

voidcvMulticlassPathPlanner::find_paths (cv::Point start, cv::Point goal, int nPaths=1, bool vis=false, int obsSizeThresh=0);

obsSizeThresh is the minimum size of obstacle that would create multiple classes of paths. The shortest path is stored in the member std::vector< std::vector< cv::Point > > paths. You can directly draw the path in a matrix using either of the following functions:

voidcvMulticlassPathPlanner::draw_paths (cv::Mat& in_map, cv::Mat& out_map, ...);
cv::Mat cvMulticlassPathPlanner::draw_paths (...)

See the example in examples-dosl/src/simple/map2d_encapsulated_HomotopyPathPlanning.cpp for more details.


Documentation:

DOSL wiki is under construction: https://github.com/subh83/DOSL/wiki


Citation:

If you found this library useful in your research, please cite it in your paper as follows:

Suggested citation format: Subhrajit Bhattacharya, "Discrete Optimal Search Library (DOSL): A template-based C++ library for discrete optimal search", 2017. Available at https://github.com/subh83/DOSL .

Bibtex entry:

 @misc{dosl,
title = {Discrete Optimal Search Library (DOSL): A template-based C++ library for discrete optimal search},
author = {Subhrajit Bhattacharya},
note = {Available at https://github.com/subh83/DOSL },
url = { https://github.com/subh83/DOSL },
year = {2017}
}

If you, in particular, use the S-star search algorithm, please use the following citation:

Subhrajit Bhattacharya, "Towards optimal path computation in a simplicial complex", The International Journal of Robotics Research (IJRR), online-first, June, 2019. DOI: 10.1177/0278364919855422.

Bibtex entry:

@ARTICLE { Simplicial:star:19,
AUTHOR = { Subhrajit Bhattacharya },
TITLE = { Towards optimal path computation in a simplicial complex },
JOURNAL = { The International Journal of Robotics Research (IJRR) },
MONTH = { June },
YEAR = { 2019 },
NOTE = { DOI: 10.1177/0278364919855422 },
URL = { https://doi.org/10.1177/0278364919855422 }
}

Version history:

  • Jan 2023: version 3.32: Graph directionality auto-correct bug-fix in SStar; Added demonstration of use of non-zero heuristic function in example program and the bare-bones example.

  • May 2019: version 3.31: 'multiple definition' bug fix.

  • May 2019: version 3.3 released: virtual functions replaced by CRTP in [AlgName]::Algorithm classes, thus making some programs run twice as fast; replaced node and simplex containers/heaps with pointers, making way for multi-thread programming in future; renaming of some variables and functions (back compatibility included); bug fixes.

  • Nov 2018: version 3.26 released: Some bug fixes. Changes made to encapsulation member functions.

  • Sep 2017: version 3.25 released: Added ThetaStar and SStar search algorithms; Organized components of algorithm under nested classes; More extensive and organized examples grouped into "simple" and "advanced"; Simple encapsulations for path planning in OpenCV matrices added.

  • May 2017: version 3.1 released

  • Nov 2016: version 3.0a released

  • Discrete Optimal Search Library (DOSL) is a fork of the Yet Another Graph-Search Based Planning Library (YAGSBPL) hosted at https://github.com/subh83/YAGSBPL . YAGSBPL is now deprecated.


License:

/** **************************************************************************************
* *
* Part of *
* Discrete Optimal Search Library (DOSL) *
* A template-based C++ library for discrete search *
* Version 3.x *
* ---------------------------------------------------------- *
* Copyright (C) 2017 Subhrajit Bhattacharya *
* *
* This program 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 3 of the License, or *
* (at your option) any later version. *
* *
* This program 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 <http://www.gnu.org/licenses/>. *
* *
* *
* Contact: subhrajit@gmail.com *
* https://www.lehigh.edu/~sub216/ , http://subhrajit.net/ *
* *
* *
*************************************************************************************** **/

About

Discrete Optimal Search Library (DOSL): A template-based C++ library for searching, path-finding and exploring discrete spaces such as graphs and simplicial complexes

Resources

Stars

50 stars

Watchers

5 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Latest commit

History

65 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Discrete Optimal Search Library (DOSL)

subh83/DOSL

Description:

"Discrete Optimal Search Library (DOSL)" is a fast, efficient and easy-to-use library for construction of discrete representation (e.g., a graph or a simplicial complex) and search (e.g., using algorithms like A-star, Dijkstra's, etc.) library written in C++, designed specifically for searching medium to large scale graphs or simplicial complexes for optimal paths.


Output from example program map2d_PathPlanning showing the progress of A* search algorithm in finding shortest path in an 8-connected grid graph and using an Euclidean heuristic function (requires OpenCV):

5000 vertices expanded10000 vertices expanded15000 vertices expandedFinal path

See the Overview of Selected Example Programs for more sample outputs from the provided example programs.


DOSL is designed to be:

  • Fast (e.g., with integer coordinates for nodes but floating point cost as well as cost function needing to perform floating point operations online, and an average degree of the graph being 8, the A* search algorithm of the library can expand about 150,000 nodes in the graph in about 0.53 second on a 1.8GHz quad-core processor machine with 16GB RAM.)
  • Easy to use / versatile (being template-based, defining new arbitrary node-types, cost types, etc. is made easy. For graph connectivity, node accessibility tests, etc, user-defined classes can be used, which makes defining the graph structure very easy, yet highly flexible.)

DOSL supports:

  • Directed graphs with complex cost functions.
  • On the fly graph construction (i.e. no need to construct and store a complete graph before starting the search/planning process - makes it highly suitable for RRT-like graph construction).
  • Arbitrary graph node type declaration.
  • Arbitrary edge cost type declaration.
  • Intermediate storage of paths during graph search.
  • Multiple goals (or goal manifold) that determine when to stop search.
  • Multiple start nodes for wave-front expansion type graph exploration.
  • Event handling (i.e., call to user-defined functions upon generation, g-score updating or expansion of a node during the search process).
  • Other forms of discrete representations, such as simplicial complexes (S-star algorithm), is supported by specific planners.
  • Ability to write new planners with ease. Comes with a weighted A-star (that includes Dijkstra's and normal A-star), Theta-star and S-star planner by default.

New:

  • The [AlgName]::Algorithm class template now takes in the derived class as its first template parameter (a CRTP). Thus, definition of a searchProblem class should now be made as follows:
classsearchProblem : publicAStar::Algorithm <searchProblem, myNode, double>
{ /* ... */}; // ^^^^ new template parameter

This change (replacing virtual functions with CRTP) has helped some example programs to run twice as fast.
Backward compatibility note (since v3.3): Users need to make this change for backward compatibility.
See the example programs for illustration and details.

NOTE: Discrete Optimal Search Library (DOSL) is a fork of the Yet Another Graph-Search Based Planning Library (YAGSBPL) hosted at https://github.com/subh83/YAGSBPL . YAGSBPL is now deprecated.


Installation and Compilation of Examples:

Installation (optional): DOSL is (to a large extent) template-based. There is nothing to build for the library itself. Simply include the header "dosl/dosl" in your C++ code to use DOSL. You can (optionally) install the headers in the system folder by running

 sudo make install

Compilation: Quick compilation of all the examples in the examples-dosl folder (this will ask you to choose an algorithm):

 cd examples-dosl
make all

Alternatively, run make simple or make advanced to compile a selection of the example programs only (see examples-dosl/makefile for other make rules). All executables are created in the examples-dosl/bin folder.

Running the Examples: After compilation, to run an example program:

 ./bin/<program_name>

Or to run all compiled example programs under the ./bin folder:

 make run

Basic Usage:

DOSL uses graphs as the most basic form of discrete representation. In order to describe a graph to DOSL, the user needs to define two classes:

  1. A class defining the type of node/vertex in the graph. This class needs to be derived from DOSL's [AlgName]::Node class (where[AlgName] is the name of the algorithm being used). The user must overload operator== for this class so that DOSL knows how to tell whether two nodes are the same or different.

  2. A class defining the other aspects of the graph structure and the search problem. This class needs to be derived from DOSL's [AlgName]::Algorithm class. Typical members of this class (virtual members of [AlgName]::Algorithm) that the user needs to define are getSuccessors, getStartNodes, stopSearch and getHeuristics.

Below is a bare-bones example, with explanations in comments, illustrating the use of DOSL with the AStar algorithm:

// standard headers
#include<stdio.h>
#include<math.h>
#include<vector>
#include<iostream>// DOSL headers:
#include<dosl/dosl>// ==============================================================================/* The following class defines the type of a vertex of the graph. Needs to be derived from DOSL-provided class template 'AStar::Node<node_type,cost_type>' */classmyNode : publicAStar::Node<myNode,double>
{
public:int x, y; // (x,y) coordinates defining a point on plane.myNode () { }
myNode (int xx, int yy) : x(xx), y(yy) { }
// The comparison operator must be defined for the node type.booloperator==(const myNode& n) const { return (x==n.x && y==n.y); }
// An efficint hash function, 'getHashBin', for node type is desired, but is optional.intgetHashBin (void) { return (abs(((int)x>>4) + ((int)y<<3) + ((int)x<<4) + ((int)y>>3))); }
// optional printing function, 'print':voidprint (std::string head="", std::string tail="") const
{ _dosl_cout << head << "x=" << x << ", y=" << y << _dosl_endl; }
};
// ==============================================================================/* The following class contains the description of the graph (graph connectivity) and search problem description (start and stop criteria). Needs to be derived from DOSL-provided class template 'AStar::Algorithm<search_problem_class,node_type,cost_type>' */classsearchProblem : publicAStar::Algorithm<searchProblem,myNode,double>
{
public:// user-defined problem parameters:
myNode goal_node;
// Constructors, if anysearchProblem () { goal_node = myNode(150,100); }
// -----------------------------------------------------------/* The following functions are use by the base class 'AStar::Algorithm' to determine  graph structure and search parameters. *//* Prototype for 'AStar::Algorithm<>::getSuccessors': void getSuccessors  (NodeType& n, std::vector<NodeType>* const s, std::vector<CostType>* const s); Description: Takes in a vertex, n, and returns its neighbors/successors, s,  and the costs/distances of the edges, c. This defines graph connectivity. */voidgetSuccessors (myNode &n, std::vector<myNode>* s, std::vector<double>* c) {
// This function should account for obstacles, constraints and size of environment.
myNode tn;
for (int a=-1; a<=1; a++)
for (int b=-1; b<=1; b++) {
if (a==0 && b==0) continue;
tn.x = n.x + a;
tn.y = n.y + b;
s->push_back(tn);
c->push_back(sqrt((double)(a*a+b*b))); }
}
/* Prototype for 'AStar::Algorithm<>::getStartNodes': std::vector<NodeType> getStartNodes (void); Description: Should return the list of vertices(s) to start the search with. */
std::vector<myNode> getStartNodes (void) {
std::vector<myNode> startNodes;
for (int a=0; a<1; ++a) {
myNode tn (0, 0); // start node
startNodes.push_back (tn);
}
return (startNodes);
}
// Optional Functions:/* Prototype for 'AStar::Algorithm<>::stopSearch': bool stopSearch (NodeType& n); Description: Determines whether to stop the search when 'n' is being expanded. Optional -- If not provided, search will terminate only when heap is empty. */boolstopSearch (myNode &n) {
return (n==goal_node);
}
/* Prototype for 'AStar::Algorithm<>::getHeuristics': CostType getHeuristics (NodeType& n); Description: Heuristic function for the search. Optional -- If not provided, a zero heuristic is assumed. */doublegetHeuristics (myNode& n) {
double dx = goal_node.x - n.x;
double dy = goal_node.y - n.y;
return (sqrt(dx*dx + dy*dy)); // Euclidean heuristic function
}
};
// ==============================================================================intmain(int argc, char *argv[])
{
searchProblem test_search_problem; // declare an instance of the search problem.
test_search_problem.search(); // execute search.// get path from start to goal vertex.
std::vector<myNode*> path = test_search_problem.reconstruct_pointer_path (test_search_problem.goal_node);
// print pathprintf ("\nPath: \n[");
for (int a=path.size()-1; a>=0; --a) {
std::cout << "[" << path[a]->x << ", " << path[a]->y << "]";
if (a>0) std::cout << "; ";
else std::cout << "]\n\n";
}
return (1);
}

Encapsulations:

For common types of graph, encaptulations can be used to hide some of the details involved in defining the graph type. Currently there are two encaptsulations that are provided with DOSL:

Single shortest path planning in an OpenCV image:

Include dosl/encapsulations/cvPathPlanner.tcc in your code. Then you can compute the optimal path from a start pixel to a goal pixel in a OpenCV image obs_map by creating a variable of type cvPathPlanner<[AlgName]> (where [AlgName] is the name of the search algorithm) using the constructor

cvPathPlanner::cvPathPlanner (cv::Mat obs_map, ...);

and then calling the member

voidcvPathPlanner::find_path (cv::Point start, cv::Point goal, bool vis=false);

Set vis to true to visualize the search. The shortest path is stored in the member std::vector<cv::Point> path. You can directly draw the path in a matrix using either of the following functions:

voidcvPathPlanner::draw_path (cv::Mat& in_map, cv::Mat& out_map, ...);
cv::Mat cvPathPlanner::draw_path (...);

See the example in examples-dosl/src/simple/map2d_encapsulated_PathPlanning.cpp for more details.

Multiple shortest paths in different topological classes in an OpenCV image:

Include dosl/encapsulations/cvMulticlassPathPlanner.tcc in your code. Then create an instance of cvMulticlassPathPlanner<[AlgName]> (where [AlgName] is the name of the search algorithm) using the constructor

cvMulticlassPathPlanner::cvMulticlassPathPlanner (cv::Mat obs_map, ...)

and compute the paths using member

voidcvMulticlassPathPlanner::find_paths (cv::Point start, cv::Point goal, int nPaths=1, bool vis=false, int obsSizeThresh=0);

obsSizeThresh is the minimum size of obstacle that would create multiple classes of paths. The shortest path is stored in the member std::vector< std::vector< cv::Point > > paths. You can directly draw the path in a matrix using either of the following functions:

voidcvMulticlassPathPlanner::draw_paths (cv::Mat& in_map, cv::Mat& out_map, ...);
cv::Mat cvMulticlassPathPlanner::draw_paths (...)

See the example in examples-dosl/src/simple/map2d_encapsulated_HomotopyPathPlanning.cpp for more details.


Documentation:

DOSL wiki is under construction: https://github.com/subh83/DOSL/wiki


Citation:

If you found this library useful in your research, please cite it in your paper as follows:

Suggested citation format: Subhrajit Bhattacharya, "Discrete Optimal Search Library (DOSL): A template-based C++ library for discrete optimal search", 2017. Available at https://github.com/subh83/DOSL .

Bibtex entry:

 @misc{dosl,
title = {Discrete Optimal Search Library (DOSL): A template-based C++ library for discrete optimal search},
author = {Subhrajit Bhattacharya},
note = {Available at https://github.com/subh83/DOSL },
url = { https://github.com/subh83/DOSL },
year = {2017}
}

If you, in particular, use the S-star search algorithm, please use the following citation:

Subhrajit Bhattacharya, "Towards optimal path computation in a simplicial complex", The International Journal of Robotics Research (IJRR), online-first, June, 2019. DOI: 10.1177/0278364919855422.

Bibtex entry:

@ARTICLE { Simplicial:star:19,
AUTHOR = { Subhrajit Bhattacharya },
TITLE = { Towards optimal path computation in a simplicial complex },
JOURNAL = { The International Journal of Robotics Research (IJRR) },
MONTH = { June },
YEAR = { 2019 },
NOTE = { DOI: 10.1177/0278364919855422 },
URL = { https://doi.org/10.1177/0278364919855422 }
}

Version history:

  • Jan 2023: version 3.32: Graph directionality auto-correct bug-fix in SStar; Added demonstration of use of non-zero heuristic function in example program and the bare-bones example.

  • May 2019: version 3.31: 'multiple definition' bug fix.

  • May 2019: version 3.3 released: virtual functions replaced by CRTP in [AlgName]::Algorithm classes, thus making some programs run twice as fast; replaced node and simplex containers/heaps with pointers, making way for multi-thread programming in future; renaming of some variables and functions (back compatibility included); bug fixes.

  • Nov 2018: version 3.26 released: Some bug fixes. Changes made to encapsulation member functions.

  • Sep 2017: version 3.25 released: Added ThetaStar and SStar search algorithms; Organized components of algorithm under nested classes; More extensive and organized examples grouped into "simple" and "advanced"; Simple encapsulations for path planning in OpenCV matrices added.

  • May 2017: version 3.1 released

  • Nov 2016: version 3.0a released

  • Discrete Optimal Search Library (DOSL) is a fork of the Yet Another Graph-Search Based Planning Library (YAGSBPL) hosted at https://github.com/subh83/YAGSBPL . YAGSBPL is now deprecated.


License:

/** **************************************************************************************
* *
* Part of *
* Discrete Optimal Search Library (DOSL) *
* A template-based C++ library for discrete search *
* Version 3.x *
* ---------------------------------------------------------- *
* Copyright (C) 2017 Subhrajit Bhattacharya *
* *
* This program 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 3 of the License, or *
* (at your option) any later version. *
* *
* This program 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 <http://www.gnu.org/licenses/>. *
* *
* *
* Contact: subhrajit@gmail.com *
* https://www.lehigh.edu/~sub216/ , http://subhrajit.net/ *
* *
* *
*************************************************************************************** **/

About

Discrete Optimal Search Library (DOSL): A template-based C++ library for searching, path-finding and exploring discrete spaces such as graphs and simplicial complexes

Resources

Stars

50 stars

Watchers

5 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

65 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Discrete Optimal Search Library (DOSL)

subh83/DOSL

Description:

"Discrete Optimal Search Library (DOSL)" is a fast, efficient and easy-to-use library for construction of discrete representation (e.g., a graph or a simplicial complex) and search (e.g., using algorithms like A-star, Dijkstra's, etc.) library written in C++, designed specifically for searching medium to large scale graphs or simplicial complexes for optimal paths.


Output from example program map2d_PathPlanning showing the progress of A* search algorithm in finding shortest path in an 8-connected grid graph and using an Euclidean heuristic function (requires OpenCV):

5000 vertices expanded10000 vertices expanded15000 vertices expandedFinal path

See the Overview of Selected Example Programs for more sample outputs from the provided example programs.


DOSL is designed to be:

  • Fast (e.g., with integer coordinates for nodes but floating point cost as well as cost function needing to perform floating point operations online, and an average degree of the graph being 8, the A* search algorithm of the library can expand about 150,000 nodes in the graph in about 0.53 second on a 1.8GHz quad-core processor machine with 16GB RAM.)
  • Easy to use / versatile (being template-based, defining new arbitrary node-types, cost types, etc. is made easy. For graph connectivity, node accessibility tests, etc, user-defined classes can be used, which makes defining the graph structure very easy, yet highly flexible.)

DOSL supports:

  • Directed graphs with complex cost functions.
  • On the fly graph construction (i.e. no need to construct and store a complete graph before starting the search/planning process - makes it highly suitable for RRT-like graph construction).
  • Arbitrary graph node type declaration.
  • Arbitrary edge cost type declaration.
  • Intermediate storage of paths during graph search.
  • Multiple goals (or goal manifold) that determine when to stop search.
  • Multiple start nodes for wave-front expansion type graph exploration.
  • Event handling (i.e., call to user-defined functions upon generation, g-score updating or expansion of a node during the search process).
  • Other forms of discrete representations, such as simplicial complexes (S-star algorithm), is supported by specific planners.
  • Ability to write new planners with ease. Comes with a weighted A-star (that includes Dijkstra's and normal A-star), Theta-star and S-star planner by default.

New:

  • The [AlgName]::Algorithm class template now takes in the derived class as its first template parameter (a CRTP). Thus, definition of a searchProblem class should now be made as follows:
classsearchProblem : publicAStar::Algorithm <searchProblem, myNode, double>
{ /* ... */}; // ^^^^ new template parameter

This change (replacing virtual functions with CRTP) has helped some example programs to run twice as fast.
Backward compatibility note (since v3.3): Users need to make this change for backward compatibility.
See the example programs for illustration and details.

NOTE: Discrete Optimal Search Library (DOSL) is a fork of the Yet Another Graph-Search Based Planning Library (YAGSBPL) hosted at https://github.com/subh83/YAGSBPL . YAGSBPL is now deprecated.


Installation and Compilation of Examples:

Installation (optional): DOSL is (to a large extent) template-based. There is nothing to build for the library itself. Simply include the header "dosl/dosl" in your C++ code to use DOSL. You can (optionally) install the headers in the system folder by running

 sudo make install

Compilation: Quick compilation of all the examples in the examples-dosl folder (this will ask you to choose an algorithm):

 cd examples-dosl
make all

Alternatively, run make simple or make advanced to compile a selection of the example programs only (see examples-dosl/makefile for other make rules). All executables are created in the examples-dosl/bin folder.

Running the Examples: After compilation, to run an example program:

 ./bin/<program_name>

Or to run all compiled example programs under the ./bin folder:

 make run

Basic Usage:

DOSL uses graphs as the most basic form of discrete representation. In order to describe a graph to DOSL, the user needs to define two classes:

  1. A class defining the type of node/vertex in the graph. This class needs to be derived from DOSL's [AlgName]::Node class (where[AlgName] is the name of the algorithm being used). The user must overload operator== for this class so that DOSL knows how to tell whether two nodes are the same or different.

  2. A class defining the other aspects of the graph structure and the search problem. This class needs to be derived from DOSL's [AlgName]::Algorithm class. Typical members of this class (virtual members of [AlgName]::Algorithm) that the user needs to define are getSuccessors, getStartNodes, stopSearch and getHeuristics.

Below is a bare-bones example, with explanations in comments, illustrating the use of DOSL with the AStar algorithm:

// standard headers
#include<stdio.h>
#include<math.h>
#include<vector>
#include<iostream>// DOSL headers:
#include<dosl/dosl>// ==============================================================================/* The following class defines the type of a vertex of the graph. Needs to be derived from DOSL-provided class template 'AStar::Node<node_type,cost_type>' */classmyNode : publicAStar::Node<myNode,double>
{
public:int x, y; // (x,y) coordinates defining a point on plane.myNode () { }
myNode (int xx, int yy) : x(xx), y(yy) { }
// The comparison operator must be defined for the node type.booloperator==(const myNode& n) const { return (x==n.x && y==n.y); }
// An efficint hash function, 'getHashBin', for node type is desired, but is optional.intgetHashBin (void) { return (abs(((int)x>>4) + ((int)y<<3) + ((int)x<<4) + ((int)y>>3))); }
// optional printing function, 'print':voidprint (std::string head="", std::string tail="") const
{ _dosl_cout << head << "x=" << x << ", y=" << y << _dosl_endl; }
};
// ==============================================================================/* The following class contains the description of the graph (graph connectivity) and search problem description (start and stop criteria). Needs to be derived from DOSL-provided class template 'AStar::Algorithm<search_problem_class,node_type,cost_type>' */classsearchProblem : publicAStar::Algorithm<searchProblem,myNode,double>
{
public:// user-defined problem parameters:
myNode goal_node;
// Constructors, if anysearchProblem () { goal_node = myNode(150,100); }
// -----------------------------------------------------------/* The following functions are use by the base class 'AStar::Algorithm' to determine  graph structure and search parameters. *//* Prototype for 'AStar::Algorithm<>::getSuccessors': void getSuccessors  (NodeType& n, std::vector<NodeType>* const s, std::vector<CostType>* const s); Description: Takes in a vertex, n, and returns its neighbors/successors, s,  and the costs/distances of the edges, c. This defines graph connectivity. */voidgetSuccessors (myNode &n, std::vector<myNode>* s, std::vector<double>* c) {
// This function should account for obstacles, constraints and size of environment.
myNode tn;
for (int a=-1; a<=1; a++)
for (int b=-1; b<=1; b++) {
if (a==0 && b==0) continue;
tn.x = n.x + a;
tn.y = n.y + b;
s->push_back(tn);
c->push_back(sqrt((double)(a*a+b*b))); }
}
/* Prototype for 'AStar::Algorithm<>::getStartNodes': std::vector<NodeType> getStartNodes (void); Description: Should return the list of vertices(s) to start the search with. */
std::vector<myNode> getStartNodes (void) {
std::vector<myNode> startNodes;
for (int a=0; a<1; ++a) {
myNode tn (0, 0); // start node
startNodes.push_back (tn);
}
return (startNodes);
}
// Optional Functions:/* Prototype for 'AStar::Algorithm<>::stopSearch': bool stopSearch (NodeType& n); Description: Determines whether to stop the search when 'n' is being expanded. Optional -- If not provided, search will terminate only when heap is empty. */boolstopSearch (myNode &n) {
return (n==goal_node);
}
/* Prototype for 'AStar::Algorithm<>::getHeuristics': CostType getHeuristics (NodeType& n); Description: Heuristic function for the search. Optional -- If not provided, a zero heuristic is assumed. */doublegetHeuristics (myNode& n) {
double dx = goal_node.x - n.x;
double dy = goal_node.y - n.y;
return (sqrt(dx*dx + dy*dy)); // Euclidean heuristic function
}
};
// ==============================================================================intmain(int argc, char *argv[])
{
searchProblem test_search_problem; // declare an instance of the search problem.
test_search_problem.search(); // execute search.// get path from start to goal vertex.
std::vector<myNode*> path = test_search_problem.reconstruct_pointer_path (test_search_problem.goal_node);
// print pathprintf ("\nPath: \n[");
for (int a=path.size()-1; a>=0; --a) {
std::cout << "[" << path[a]->x << ", " << path[a]->y << "]";
if (a>0) std::cout << "; ";
else std::cout << "]\n\n";
}
return (1);
}

Encapsulations:

For common types of graph, encaptulations can be used to hide some of the details involved in defining the graph type. Currently there are two encaptsulations that are provided with DOSL:

Single shortest path planning in an OpenCV image:

Include dosl/encapsulations/cvPathPlanner.tcc in your code. Then you can compute the optimal path from a start pixel to a goal pixel in a OpenCV image obs_map by creating a variable of type cvPathPlanner<[AlgName]> (where [AlgName] is the name of the search algorithm) using the constructor

cvPathPlanner::cvPathPlanner (cv::Mat obs_map, ...);

and then calling the member

voidcvPathPlanner::find_path (cv::Point start, cv::Point goal, bool vis=false);

Set vis to true to visualize the search. The shortest path is stored in the member std::vector<cv::Point> path. You can directly draw the path in a matrix using either of the following functions:

voidcvPathPlanner::draw_path (cv::Mat& in_map, cv::Mat& out_map, ...);
cv::Mat cvPathPlanner::draw_path (...);

See the example in examples-dosl/src/simple/map2d_encapsulated_PathPlanning.cpp for more details.

Multiple shortest paths in different topological classes in an OpenCV image:

Include dosl/encapsulations/cvMulticlassPathPlanner.tcc in your code. Then create an instance of cvMulticlassPathPlanner<[AlgName]> (where [AlgName] is the name of the search algorithm) using the constructor

cvMulticlassPathPlanner::cvMulticlassPathPlanner (cv::Mat obs_map, ...)

and compute the paths using member

voidcvMulticlassPathPlanner::find_paths (cv::Point start, cv::Point goal, int nPaths=1, bool vis=false, int obsSizeThresh=0);

obsSizeThresh is the minimum size of obstacle that would create multiple classes of paths. The shortest path is stored in the member std::vector< std::vector< cv::Point > > paths. You can directly draw the path in a matrix using either of the following functions:

voidcvMulticlassPathPlanner::draw_paths (cv::Mat& in_map, cv::Mat& out_map, ...);
cv::Mat cvMulticlassPathPlanner::draw_paths (...)

See the example in examples-dosl/src/simple/map2d_encapsulated_HomotopyPathPlanning.cpp for more details.


Documentation:

DOSL wiki is under construction: https://github.com/subh83/DOSL/wiki


Citation:

If you found this library useful in your research, please cite it in your paper as follows:

Suggested citation format: Subhrajit Bhattacharya, "Discrete Optimal Search Library (DOSL): A template-based C++ library for discrete optimal search", 2017. Available at https://github.com/subh83/DOSL .

Bibtex entry:

 @misc{dosl,
title = {Discrete Optimal Search Library (DOSL): A template-based C++ library for discrete optimal search},
author = {Subhrajit Bhattacharya},
note = {Available at https://github.com/subh83/DOSL },
url = { https://github.com/subh83/DOSL },
year = {2017}
}

If you, in particular, use the S-star search algorithm, please use the following citation:

Subhrajit Bhattacharya, "Towards optimal path computation in a simplicial complex", The International Journal of Robotics Research (IJRR), online-first, June, 2019. DOI: 10.1177/0278364919855422.

Bibtex entry:

@ARTICLE { Simplicial:star:19,
AUTHOR = { Subhrajit Bhattacharya },
TITLE = { Towards optimal path computation in a simplicial complex },
JOURNAL = { The International Journal of Robotics Research (IJRR) },
MONTH = { June },
YEAR = { 2019 },
NOTE = { DOI: 10.1177/0278364919855422 },
URL = { https://doi.org/10.1177/0278364919855422 }
}

Version history:

  • Jan 2023: version 3.32: Graph directionality auto-correct bug-fix in SStar; Added demonstration of use of non-zero heuristic function in example program and the bare-bones example.

  • May 2019: version 3.31: 'multiple definition' bug fix.

  • May 2019: version 3.3 released: virtual functions replaced by CRTP in [AlgName]::Algorithm classes, thus making some programs run twice as fast; replaced node and simplex containers/heaps with pointers, making way for multi-thread programming in future; renaming of some variables and functions (back compatibility included); bug fixes.

  • Nov 2018: version 3.26 released: Some bug fixes. Changes made to encapsulation member functions.

  • Sep 2017: version 3.25 released: Added ThetaStar and SStar search algorithms; Organized components of algorithm under nested classes; More extensive and organized examples grouped into "simple" and "advanced"; Simple encapsulations for path planning in OpenCV matrices added.

  • May 2017: version 3.1 released

  • Nov 2016: version 3.0a released

  • Discrete Optimal Search Library (DOSL) is a fork of the Yet Another Graph-Search Based Planning Library (YAGSBPL) hosted at https://github.com/subh83/YAGSBPL . YAGSBPL is now deprecated.


License:

/** **************************************************************************************
* *
* Part of *
* Discrete Optimal Search Library (DOSL) *
* A template-based C++ library for discrete search *
* Version 3.x *
* ---------------------------------------------------------- *
* Copyright (C) 2017 Subhrajit Bhattacharya *
* *
* This program 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 3 of the License, or *
* (at your option) any later version. *
* *
* This program 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 <http://www.gnu.org/licenses/>. *
* *
* *
* Contact: subhrajit@gmail.com *
* https://www.lehigh.edu/~sub216/ , http://subhrajit.net/ *
* *
* *
*************************************************************************************** **/

About

Discrete Optimal Search Library (DOSL): A template-based C++ library for searching, path-finding and exploring discrete spaces such as graphs and simplicial complexes

Resources

Stars

50 stars

Watchers

5 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

65 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Discrete Optimal Search Library (DOSL)

subh83/DOSL

Description:

"Discrete Optimal Search Library (DOSL)" is a fast, efficient and easy-to-use library for construction of discrete representation (e.g., a graph or a simplicial complex) and search (e.g., using algorithms like A-star, Dijkstra's, etc.) library written in C++, designed specifically for searching medium to large scale graphs or simplicial complexes for optimal paths.


Output from example program map2d_PathPlanning showing the progress of A* search algorithm in finding shortest path in an 8-connected grid graph and using an Euclidean heuristic function (requires OpenCV):

5000 vertices expanded10000 vertices expanded15000 vertices expandedFinal path

See the Overview of Selected Example Programs for more sample outputs from the provided example programs.


DOSL is designed to be:

  • Fast (e.g., with integer coordinates for nodes but floating point cost as well as cost function needing to perform floating point operations online, and an average degree of the graph being 8, the A* search algorithm of the library can expand about 150,000 nodes in the graph in about 0.53 second on a 1.8GHz quad-core processor machine with 16GB RAM.)
  • Easy to use / versatile (being template-based, defining new arbitrary node-types, cost types, etc. is made easy. For graph connectivity, node accessibility tests, etc, user-defined classes can be used, which makes defining the graph structure very easy, yet highly flexible.)

DOSL supports:

  • Directed graphs with complex cost functions.
  • On the fly graph construction (i.e. no need to construct and store a complete graph before starting the search/planning process - makes it highly suitable for RRT-like graph construction).
  • Arbitrary graph node type declaration.
  • Arbitrary edge cost type declaration.
  • Intermediate storage of paths during graph search.
  • Multiple goals (or goal manifold) that determine when to stop search.
  • Multiple start nodes for wave-front expansion type graph exploration.
  • Event handling (i.e., call to user-defined functions upon generation, g-score updating or expansion of a node during the search process).
  • Other forms of discrete representations, such as simplicial complexes (S-star algorithm), is supported by specific planners.
  • Ability to write new planners with ease. Comes with a weighted A-star (that includes Dijkstra's and normal A-star), Theta-star and S-star planner by default.

New:

  • The [AlgName]::Algorithm class template now takes in the derived class as its first template parameter (a CRTP). Thus, definition of a searchProblem class should now be made as follows:
classsearchProblem : publicAStar::Algorithm <searchProblem, myNode, double>
{ /* ... */}; // ^^^^ new template parameter

This change (replacing virtual functions with CRTP) has helped some example programs to run twice as fast.
Backward compatibility note (since v3.3): Users need to make this change for backward compatibility.
See the example programs for illustration and details.

NOTE: Discrete Optimal Search Library (DOSL) is a fork of the Yet Another Graph-Search Based Planning Library (YAGSBPL) hosted at https://github.com/subh83/YAGSBPL . YAGSBPL is now deprecated.


Installation and Compilation of Examples:

Installation (optional): DOSL is (to a large extent) template-based. There is nothing to build for the library itself. Simply include the header "dosl/dosl" in your C++ code to use DOSL. You can (optionally) install the headers in the system folder by running

 sudo make install

Compilation: Quick compilation of all the examples in the examples-dosl folder (this will ask you to choose an algorithm):

 cd examples-dosl
make all

Alternatively, run make simple or make advanced to compile a selection of the example programs only (see examples-dosl/makefile for other make rules). All executables are created in the examples-dosl/bin folder.

Running the Examples: After compilation, to run an example program:

 ./bin/<program_name>

Or to run all compiled example programs under the ./bin folder:

 make run

Basic Usage:

DOSL uses graphs as the most basic form of discrete representation. In order to describe a graph to DOSL, the user needs to define two classes:

  1. A class defining the type of node/vertex in the graph. This class needs to be derived from DOSL's [AlgName]::Node class (where[AlgName] is the name of the algorithm being used). The user must overload operator== for this class so that DOSL knows how to tell whether two nodes are the same or different.

  2. A class defining the other aspects of the graph structure and the search problem. This class needs to be derived from DOSL's [AlgName]::Algorithm class. Typical members of this class (virtual members of [AlgName]::Algorithm) that the user needs to define are getSuccessors, getStartNodes, stopSearch and getHeuristics.

Below is a bare-bones example, with explanations in comments, illustrating the use of DOSL with the AStar algorithm:

// standard headers
#include<stdio.h>
#include<math.h>
#include<vector>
#include<iostream>// DOSL headers:
#include<dosl/dosl>// ==============================================================================/* The following class defines the type of a vertex of the graph. Needs to be derived from DOSL-provided class template 'AStar::Node<node_type,cost_type>' */classmyNode : publicAStar::Node<myNode,double>
{
public:int x, y; // (x,y) coordinates defining a point on plane.myNode () { }
myNode (int xx, int yy) : x(xx), y(yy) { }
// The comparison operator must be defined for the node type.booloperator==(const myNode& n) const { return (x==n.x && y==n.y); }
// An efficint hash function, 'getHashBin', for node type is desired, but is optional.intgetHashBin (void) { return (abs(((int)x>>4) + ((int)y<<3) + ((int)x<<4) + ((int)y>>3))); }
// optional printing function, 'print':voidprint (std::string head="", std::string tail="") const
{ _dosl_cout << head << "x=" << x << ", y=" << y << _dosl_endl; }
};
// ==============================================================================/* The following class contains the description of the graph (graph connectivity) and search problem description (start and stop criteria). Needs to be derived from DOSL-provided class template 'AStar::Algorithm<search_problem_class,node_type,cost_type>' */classsearchProblem : publicAStar::Algorithm<searchProblem,myNode,double>
{
public:// user-defined problem parameters:
myNode goal_node;
// Constructors, if anysearchProblem () { goal_node = myNode(150,100); }
// -----------------------------------------------------------/* The following functions are use by the base class 'AStar::Algorithm' to determine  graph structure and search parameters. *//* Prototype for 'AStar::Algorithm<>::getSuccessors': void getSuccessors  (NodeType& n, std::vector<NodeType>* const s, std::vector<CostType>* const s); Description: Takes in a vertex, n, and returns its neighbors/successors, s,  and the costs/distances of the edges, c. This defines graph connectivity. */voidgetSuccessors (myNode &n, std::vector<myNode>* s, std::vector<double>* c) {
// This function should account for obstacles, constraints and size of environment.
myNode tn;
for (int a=-1; a<=1; a++)
for (int b=-1; b<=1; b++) {
if (a==0 && b==0) continue;
tn.x = n.x + a;
tn.y = n.y + b;
s->push_back(tn);
c->push_back(sqrt((double)(a*a+b*b))); }
}
/* Prototype for 'AStar::Algorithm<>::getStartNodes': std::vector<NodeType> getStartNodes (void); Description: Should return the list of vertices(s) to start the search with. */
std::vector<myNode> getStartNodes (void) {
std::vector<myNode> startNodes;
for (int a=0; a<1; ++a) {
myNode tn (0, 0); // start node
startNodes.push_back (tn);
}
return (startNodes);
}
// Optional Functions:/* Prototype for 'AStar::Algorithm<>::stopSearch': bool stopSearch (NodeType& n); Description: Determines whether to stop the search when 'n' is being expanded. Optional -- If not provided, search will terminate only when heap is empty. */boolstopSearch (myNode &n) {
return (n==goal_node);
}
/* Prototype for 'AStar::Algorithm<>::getHeuristics': CostType getHeuristics (NodeType& n); Description: Heuristic function for the search. Optional -- If not provided, a zero heuristic is assumed. */doublegetHeuristics (myNode& n) {
double dx = goal_node.x - n.x;
double dy = goal_node.y - n.y;
return (sqrt(dx*dx + dy*dy)); // Euclidean heuristic function
}
};
// ==============================================================================intmain(int argc, char *argv[])
{
searchProblem test_search_problem; // declare an instance of the search problem.
test_search_problem.search(); // execute search.// get path from start to goal vertex.
std::vector<myNode*> path = test_search_problem.reconstruct_pointer_path (test_search_problem.goal_node);
// print pathprintf ("\nPath: \n[");
for (int a=path.size()-1; a>=0; --a) {
std::cout << "[" << path[a]->x << ", " << path[a]->y << "]";
if (a>0) std::cout << "; ";
else std::cout << "]\n\n";
}
return (1);
}

Encapsulations:

For common types of graph, encaptulations can be used to hide some of the details involved in defining the graph type. Currently there are two encaptsulations that are provided with DOSL:

Single shortest path planning in an OpenCV image:

Include dosl/encapsulations/cvPathPlanner.tcc in your code. Then you can compute the optimal path from a start pixel to a goal pixel in a OpenCV image obs_map by creating a variable of type cvPathPlanner<[AlgName]> (where [AlgName] is the name of the search algorithm) using the constructor

cvPathPlanner::cvPathPlanner (cv::Mat obs_map, ...);

and then calling the member

voidcvPathPlanner::find_path (cv::Point start, cv::Point goal, bool vis=false);

Set vis to true to visualize the search. The shortest path is stored in the member std::vector<cv::Point> path. You can directly draw the path in a matrix using either of the following functions:

voidcvPathPlanner::draw_path (cv::Mat& in_map, cv::Mat& out_map, ...);
cv::Mat cvPathPlanner::draw_path (...);

See the example in examples-dosl/src/simple/map2d_encapsulated_PathPlanning.cpp for more details.

Multiple shortest paths in different topological classes in an OpenCV image:

Include dosl/encapsulations/cvMulticlassPathPlanner.tcc in your code. Then create an instance of cvMulticlassPathPlanner<[AlgName]> (where [AlgName] is the name of the search algorithm) using the constructor

cvMulticlassPathPlanner::cvMulticlassPathPlanner (cv::Mat obs_map, ...)

and compute the paths using member

voidcvMulticlassPathPlanner::find_paths (cv::Point start, cv::Point goal, int nPaths=1, bool vis=false, int obsSizeThresh=0);

obsSizeThresh is the minimum size of obstacle that would create multiple classes of paths. The shortest path is stored in the member std::vector< std::vector< cv::Point > > paths. You can directly draw the path in a matrix using either of the following functions:

voidcvMulticlassPathPlanner::draw_paths (cv::Mat& in_map, cv::Mat& out_map, ...);
cv::Mat cvMulticlassPathPlanner::draw_paths (...)

See the example in examples-dosl/src/simple/map2d_encapsulated_HomotopyPathPlanning.cpp for more details.


Documentation:

DOSL wiki is under construction: https://github.com/subh83/DOSL/wiki


Citation:

If you found this library useful in your research, please cite it in your paper as follows:

Suggested citation format: Subhrajit Bhattacharya, "Discrete Optimal Search Library (DOSL): A template-based C++ library for discrete optimal search", 2017. Available at https://github.com/subh83/DOSL .

Bibtex entry:

 @misc{dosl,
title = {Discrete Optimal Search Library (DOSL): A template-based C++ library for discrete optimal search},
author = {Subhrajit Bhattacharya},
note = {Available at https://github.com/subh83/DOSL },
url = { https://github.com/subh83/DOSL },
year = {2017}
}

If you, in particular, use the S-star search algorithm, please use the following citation:

Subhrajit Bhattacharya, "Towards optimal path computation in a simplicial complex", The International Journal of Robotics Research (IJRR), online-first, June, 2019. DOI: 10.1177/0278364919855422.

Bibtex entry:

@ARTICLE { Simplicial:star:19,
AUTHOR = { Subhrajit Bhattacharya },
TITLE = { Towards optimal path computation in a simplicial complex },
JOURNAL = { The International Journal of Robotics Research (IJRR) },
MONTH = { June },
YEAR = { 2019 },
NOTE = { DOI: 10.1177/0278364919855422 },
URL = { https://doi.org/10.1177/0278364919855422 }
}

Version history:

  • Jan 2023: version 3.32: Graph directionality auto-correct bug-fix in SStar; Added demonstration of use of non-zero heuristic function in example program and the bare-bones example.

  • May 2019: version 3.31: 'multiple definition' bug fix.

  • May 2019: version 3.3 released: virtual functions replaced by CRTP in [AlgName]::Algorithm classes, thus making some programs run twice as fast; replaced node and simplex containers/heaps with pointers, making way for multi-thread programming in future; renaming of some variables and functions (back compatibility included); bug fixes.

  • Nov 2018: version 3.26 released: Some bug fixes. Changes made to encapsulation member functions.

  • Sep 2017: version 3.25 released: Added ThetaStar and SStar search algorithms; Organized components of algorithm under nested classes; More extensive and organized examples grouped into "simple" and "advanced"; Simple encapsulations for path planning in OpenCV matrices added.

  • May 2017: version 3.1 released

  • Nov 2016: version 3.0a released

  • Discrete Optimal Search Library (DOSL) is a fork of the Yet Another Graph-Search Based Planning Library (YAGSBPL) hosted at https://github.com/subh83/YAGSBPL . YAGSBPL is now deprecated.


License:

/** **************************************************************************************
* *
* Part of *
* Discrete Optimal Search Library (DOSL) *
* A template-based C++ library for discrete search *
* Version 3.x *
* ---------------------------------------------------------- *
* Copyright (C) 2017 Subhrajit Bhattacharya *
* *
* This program 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 3 of the License, or *
* (at your option) any later version. *
* *
* This program 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 <http://www.gnu.org/licenses/>. *
* *
* *
* Contact: subhrajit@gmail.com *
* https://www.lehigh.edu/~sub216/ , http://subhrajit.net/ *
* *
* *
*************************************************************************************** **/

About

Discrete Optimal Search Library (DOSL): A template-based C++ library for searching, path-finding and exploring discrete spaces such as graphs and simplicial complexes

Resources

Stars

50 stars

Watchers

5 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Latest commit

History

65 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Discrete Optimal Search Library (DOSL)

subh83/DOSL

Description:

"Discrete Optimal Search Library (DOSL)" is a fast, efficient and easy-to-use library for construction of discrete representation (e.g., a graph or a simplicial complex) and search (e.g., using algorithms like A-star, Dijkstra's, etc.) library written in C++, designed specifically for searching medium to large scale graphs or simplicial complexes for optimal paths.


Output from example program map2d_PathPlanning showing the progress of A* search algorithm in finding shortest path in an 8-connected grid graph and using an Euclidean heuristic function (requires OpenCV):

5000 vertices expanded10000 vertices expanded15000 vertices expandedFinal path

See the Overview of Selected Example Programs for more sample outputs from the provided example programs.


DOSL is designed to be:

  • Fast (e.g., with integer coordinates for nodes but floating point cost as well as cost function needing to perform floating point operations online, and an average degree of the graph being 8, the A* search algorithm of the library can expand about 150,000 nodes in the graph in about 0.53 second on a 1.8GHz quad-core processor machine with 16GB RAM.)
  • Easy to use / versatile (being template-based, defining new arbitrary node-types, cost types, etc. is made easy. For graph connectivity, node accessibility tests, etc, user-defined classes can be used, which makes defining the graph structure very easy, yet highly flexible.)

DOSL supports:

  • Directed graphs with complex cost functions.
  • On the fly graph construction (i.e. no need to construct and store a complete graph before starting the search/planning process - makes it highly suitable for RRT-like graph construction).
  • Arbitrary graph node type declaration.
  • Arbitrary edge cost type declaration.
  • Intermediate storage of paths during graph search.
  • Multiple goals (or goal manifold) that determine when to stop search.
  • Multiple start nodes for wave-front expansion type graph exploration.
  • Event handling (i.e., call to user-defined functions upon generation, g-score updating or expansion of a node during the search process).
  • Other forms of discrete representations, such as simplicial complexes (S-star algorithm), is supported by specific planners.
  • Ability to write new planners with ease. Comes with a weighted A-star (that includes Dijkstra's and normal A-star), Theta-star and S-star planner by default.

New:

  • The [AlgName]::Algorithm class template now takes in the derived class as its first template parameter (a CRTP). Thus, definition of a searchProblem class should now be made as follows:
classsearchProblem : publicAStar::Algorithm <searchProblem, myNode, double>
{ /* ... */}; // ^^^^ new template parameter

This change (replacing virtual functions with CRTP) has helped some example programs to run twice as fast.
Backward compatibility note (since v3.3): Users need to make this change for backward compatibility.
See the example programs for illustration and details.

NOTE: Discrete Optimal Search Library (DOSL) is a fork of the Yet Another Graph-Search Based Planning Library (YAGSBPL) hosted at https://github.com/subh83/YAGSBPL . YAGSBPL is now deprecated.


Installation and Compilation of Examples:

Installation (optional): DOSL is (to a large extent) template-based. There is nothing to build for the library itself. Simply include the header "dosl/dosl" in your C++ code to use DOSL. You can (optionally) install the headers in the system folder by running

 sudo make install

Compilation: Quick compilation of all the examples in the examples-dosl folder (this will ask you to choose an algorithm):

 cd examples-dosl
make all

Alternatively, run make simple or make advanced to compile a selection of the example programs only (see examples-dosl/makefile for other make rules). All executables are created in the examples-dosl/bin folder.

Running the Examples: After compilation, to run an example program:

 ./bin/<program_name>

Or to run all compiled example programs under the ./bin folder:

 make run

Basic Usage:

DOSL uses graphs as the most basic form of discrete representation. In order to describe a graph to DOSL, the user needs to define two classes:

  1. A class defining the type of node/vertex in the graph. This class needs to be derived from DOSL's [AlgName]::Node class (where[AlgName] is the name of the algorithm being used). The user must overload operator== for this class so that DOSL knows how to tell whether two nodes are the same or different.

  2. A class defining the other aspects of the graph structure and the search problem. This class needs to be derived from DOSL's [AlgName]::Algorithm class. Typical members of this class (virtual members of [AlgName]::Algorithm) that the user needs to define are getSuccessors, getStartNodes, stopSearch and getHeuristics.

Below is a bare-bones example, with explanations in comments, illustrating the use of DOSL with the AStar algorithm:

// standard headers
#include<stdio.h>
#include<math.h>
#include<vector>
#include<iostream>// DOSL headers:
#include<dosl/dosl>// ==============================================================================/* The following class defines the type of a vertex of the graph. Needs to be derived from DOSL-provided class template 'AStar::Node<node_type,cost_type>' */classmyNode : publicAStar::Node<myNode,double>
{
public:int x, y; // (x,y) coordinates defining a point on plane.myNode () { }
myNode (int xx, int yy) : x(xx), y(yy) { }
// The comparison operator must be defined for the node type.booloperator==(const myNode& n) const { return (x==n.x && y==n.y); }
// An efficint hash function, 'getHashBin', for node type is desired, but is optional.intgetHashBin (void) { return (abs(((int)x>>4) + ((int)y<<3) + ((int)x<<4) + ((int)y>>3))); }
// optional printing function, 'print':voidprint (std::string head="", std::string tail="") const
{ _dosl_cout << head << "x=" << x << ", y=" << y << _dosl_endl; }
};
// ==============================================================================/* The following class contains the description of the graph (graph connectivity) and search problem description (start and stop criteria). Needs to be derived from DOSL-provided class template 'AStar::Algorithm<search_problem_class,node_type,cost_type>' */classsearchProblem : publicAStar::Algorithm<searchProblem,myNode,double>
{
public:// user-defined problem parameters:
myNode goal_node;
// Constructors, if anysearchProblem () { goal_node = myNode(150,100); }
// -----------------------------------------------------------/* The following functions are use by the base class 'AStar::Algorithm' to determine  graph structure and search parameters. *//* Prototype for 'AStar::Algorithm<>::getSuccessors': void getSuccessors  (NodeType& n, std::vector<NodeType>* const s, std::vector<CostType>* const s); Description: Takes in a vertex, n, and returns its neighbors/successors, s,  and the costs/distances of the edges, c. This defines graph connectivity. */voidgetSuccessors (myNode &n, std::vector<myNode>* s, std::vector<double>* c) {
// This function should account for obstacles, constraints and size of environment.
myNode tn;
for (int a=-1; a<=1; a++)
for (int b=-1; b<=1; b++) {
if (a==0 && b==0) continue;
tn.x = n.x + a;
tn.y = n.y + b;
s->push_back(tn);
c->push_back(sqrt((double)(a*a+b*b))); }
}
/* Prototype for 'AStar::Algorithm<>::getStartNodes': std::vector<NodeType> getStartNodes (void); Description: Should return the list of vertices(s) to start the search with. */
std::vector<myNode> getStartNodes (void) {
std::vector<myNode> startNodes;
for (int a=0; a<1; ++a) {
myNode tn (0, 0); // start node
startNodes.push_back (tn);
}
return (startNodes);
}
// Optional Functions:/* Prototype for 'AStar::Algorithm<>::stopSearch': bool stopSearch (NodeType& n); Description: Determines whether to stop the search when 'n' is being expanded. Optional -- If not provided, search will terminate only when heap is empty. */boolstopSearch (myNode &n) {
return (n==goal_node);
}
/* Prototype for 'AStar::Algorithm<>::getHeuristics': CostType getHeuristics (NodeType& n); Description: Heuristic function for the search. Optional -- If not provided, a zero heuristic is assumed. */doublegetHeuristics (myNode& n) {
double dx = goal_node.x - n.x;
double dy = goal_node.y - n.y;
return (sqrt(dx*dx + dy*dy)); // Euclidean heuristic function
}
};
// ==============================================================================intmain(int argc, char *argv[])
{
searchProblem test_search_problem; // declare an instance of the search problem.
test_search_problem.search(); // execute search.// get path from start to goal vertex.
std::vector<myNode*> path = test_search_problem.reconstruct_pointer_path (test_search_problem.goal_node);
// print pathprintf ("\nPath: \n[");
for (int a=path.size()-1; a>=0; --a) {
std::cout << "[" << path[a]->x << ", " << path[a]->y << "]";
if (a>0) std::cout << "; ";
else std::cout << "]\n\n";
}
return (1);
}

Encapsulations:

For common types of graph, encaptulations can be used to hide some of the details involved in defining the graph type. Currently there are two encaptsulations that are provided with DOSL:

Single shortest path planning in an OpenCV image:

Include dosl/encapsulations/cvPathPlanner.tcc in your code. Then you can compute the optimal path from a start pixel to a goal pixel in a OpenCV image obs_map by creating a variable of type cvPathPlanner<[AlgName]> (where [AlgName] is the name of the search algorithm) using the constructor

cvPathPlanner::cvPathPlanner (cv::Mat obs_map, ...);

and then calling the member

voidcvPathPlanner::find_path (cv::Point start, cv::Point goal, bool vis=false);

Set vis to true to visualize the search. The shortest path is stored in the member std::vector<cv::Point> path. You can directly draw the path in a matrix using either of the following functions:

voidcvPathPlanner::draw_path (cv::Mat& in_map, cv::Mat& out_map, ...);
cv::Mat cvPathPlanner::draw_path (...);

See the example in examples-dosl/src/simple/map2d_encapsulated_PathPlanning.cpp for more details.

Multiple shortest paths in different topological classes in an OpenCV image:

Include dosl/encapsulations/cvMulticlassPathPlanner.tcc in your code. Then create an instance of cvMulticlassPathPlanner<[AlgName]> (where [AlgName] is the name of the search algorithm) using the constructor

cvMulticlassPathPlanner::cvMulticlassPathPlanner (cv::Mat obs_map, ...)

and compute the paths using member

voidcvMulticlassPathPlanner::find_paths (cv::Point start, cv::Point goal, int nPaths=1, bool vis=false, int obsSizeThresh=0);

obsSizeThresh is the minimum size of obstacle that would create multiple classes of paths. The shortest path is stored in the member std::vector< std::vector< cv::Point > > paths. You can directly draw the path in a matrix using either of the following functions:

voidcvMulticlassPathPlanner::draw_paths (cv::Mat& in_map, cv::Mat& out_map, ...);
cv::Mat cvMulticlassPathPlanner::draw_paths (...)

See the example in examples-dosl/src/simple/map2d_encapsulated_HomotopyPathPlanning.cpp for more details.


Documentation:

DOSL wiki is under construction: https://github.com/subh83/DOSL/wiki


Citation:

If you found this library useful in your research, please cite it in your paper as follows:

Suggested citation format: Subhrajit Bhattacharya, "Discrete Optimal Search Library (DOSL): A template-based C++ library for discrete optimal search", 2017. Available at https://github.com/subh83/DOSL .

Bibtex entry:

 @misc{dosl,
title = {Discrete Optimal Search Library (DOSL): A template-based C++ library for discrete optimal search},
author = {Subhrajit Bhattacharya},
note = {Available at https://github.com/subh83/DOSL },
url = { https://github.com/subh83/DOSL },
year = {2017}
}

If you, in particular, use the S-star search algorithm, please use the following citation:

Subhrajit Bhattacharya, "Towards optimal path computation in a simplicial complex", The International Journal of Robotics Research (IJRR), online-first, June, 2019. DOI: 10.1177/0278364919855422.

Bibtex entry:

@ARTICLE { Simplicial:star:19,
AUTHOR = { Subhrajit Bhattacharya },
TITLE = { Towards optimal path computation in a simplicial complex },
JOURNAL = { The International Journal of Robotics Research (IJRR) },
MONTH = { June },
YEAR = { 2019 },
NOTE = { DOI: 10.1177/0278364919855422 },
URL = { https://doi.org/10.1177/0278364919855422 }
}

Version history:

  • Jan 2023: version 3.32: Graph directionality auto-correct bug-fix in SStar; Added demonstration of use of non-zero heuristic function in example program and the bare-bones example.

  • May 2019: version 3.31: 'multiple definition' bug fix.

  • May 2019: version 3.3 released: virtual functions replaced by CRTP in [AlgName]::Algorithm classes, thus making some programs run twice as fast; replaced node and simplex containers/heaps with pointers, making way for multi-thread programming in future; renaming of some variables and functions (back compatibility included); bug fixes.

  • Nov 2018: version 3.26 released: Some bug fixes. Changes made to encapsulation member functions.

  • Sep 2017: version 3.25 released: Added ThetaStar and SStar search algorithms; Organized components of algorithm under nested classes; More extensive and organized examples grouped into "simple" and "advanced"; Simple encapsulations for path planning in OpenCV matrices added.

  • May 2017: version 3.1 released

  • Nov 2016: version 3.0a released

  • Discrete Optimal Search Library (DOSL) is a fork of the Yet Another Graph-Search Based Planning Library (YAGSBPL) hosted at https://github.com/subh83/YAGSBPL . YAGSBPL is now deprecated.


License:

/** **************************************************************************************
* *
* Part of *
* Discrete Optimal Search Library (DOSL) *
* A template-based C++ library for discrete search *
* Version 3.x *
* ---------------------------------------------------------- *
* Copyright (C) 2017 Subhrajit Bhattacharya *
* *
* This program 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 3 of the License, or *
* (at your option) any later version. *
* *
* This program 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 <http://www.gnu.org/licenses/>. *
* *
* *
* Contact: subhrajit@gmail.com *
* https://www.lehigh.edu/~sub216/ , http://subhrajit.net/ *
* *
* *
*************************************************************************************** **/

About

Discrete Optimal Search Library (DOSL): A template-based C++ library for searching, path-finding and exploring discrete spaces such as graphs and simplicial complexes

Resources

Stars

50 stars

Watchers

5 watching

Forks

Releases

Packages

Contributors

Languages