Coder Social home page Coder Social logo

dijkstar's Introduction

Dijkstar

Dijkstar is an implementation of Dijkstra's single-source shortest-paths algorithm. If a destination node is given, the algorithm halts when that node is reached; otherwise it continues until paths from the source node to all other nodes are found.

Accepts an optional cost (or "weight") function that will be called on every iteration.

Also accepts an optional heuristic function that is used to push the algorithm toward a destination instead of fanning out in every direction. Using such a heuristic function converts Dijkstra to A* (and this is where the name "Dijkstar" comes from).

Example:

>>> from dijkstar import Graph, find_path
>>> graph = Graph()
>>> graph.add_edge(1, 2, 110)
>>> graph.add_edge(2, 3, 125)
>>> graph.add_edge(3, 4, 108)
>>> find_path(graph, 1, 4)
PathInfo(
    nodes=[1, 2, 3, 4],
    edges=[110, 125, 108],
    costs=[110, 125, 108],
    total_cost=343)

In this example, the edges are just simple numeric values--110, 125, 108--that could represent lengths, such as the length of a street segment between two intersections. find_path will use these values directly as costs.

Example with cost function:

>>> from dijkstar import Graph, find_path
>>> graph = Graph()
>>> graph.add_edge(1, 2, (110, 'Main Street'))
>>> graph.add_edge(2, 3, (125, 'Main Street'))
>>> graph.add_edge(3, 4, (108, '1st Street'))
>>> def cost_func(u, v, edge, prev_edge):
...     length, name = edge
...     if prev_edge:
...         prev_name = prev_edge[1]
...     else:
...         prev_name = None
...     cost = length
...     if name != prev_name:
...         cost += 10
...     return cost
...
>>> find_path(graph, 1, 4, cost_func=cost_func)
PathInfo(
    nodes=[1, 2, 3, 4],
    edges=[(110, 'Main Street'), (125, 'Main Street'), (108, '1st Street')],
    costs=[120, 125, 118],
    total_cost=363)

The cost function is passed the current node (u), a neighbor (v) of the current node, the edge that connects u to v, and the edge that was traversed previously to get to the current node.

A cost function is most useful when computing costs dynamically. If costs in your graph are fixed, a cost function will only add unnecessary overhead. In the example above, a penalty is added when the street name changes.

When using a cost function, one recommendation is to compute a base cost when possible. For example, for a graph that represents a street network, the base cost for each street segment (edge) could be the length of the segment multiplied by the speed limit. There are two advantages to this: the size of the graph will be smaller and the cost function will be doing less work, which may improve performance.

Graph Export & Import

The graph can be saved to disk (pickled) like so:

>>> graph.dump(path)

And read back like this (load is a classmethod that returns a populated Graph instance):

>>> Graph.load(path)

Server

Dijkstar comes with a simple, standalone, web-based graph server that's built on top of Starlette and Uvicorn. It can be installed with pip:

pip install Dijkstar[server]

This installs additional libraries as well as the dijkstar serve console script. The server can be run like so:

dijkstar serve

This runs uvicorn on 127.0.0.1:8000 with an empty graph.

A previously-saved graph can be loaded from disk like so:

dijkstar serve -g path/to/graph

Server Configuration

The server is configured via environment variables following the same 12-factor pattern as Starlette. These can be set in the following ways, in order of precedence:

  • Options passed to dijkstar serve, which will overwrite existing environment variables.
  • Environment variables set in the usual way (e.g., via the shell).
  • Variables set in an env file, which will be added to the environment if not already present. The default env file is ./.env (relative to the server's PWD).

The environment variables affecting the server correspond to the settings in the dijkstar.server.conf module (with names upper-cased).

TODO: Document environment variables here.

Road Map/Planned Features

  • Console script to run server
  • Configuration via env file
  • Configuration console script options
  • Load graph from file on startup
  • Endpoints
    • HTML home page listing available endpoints
    • /graph-info -> Basic graph info
    • /load-graph -> Load a new graph (from file or data)
    • /reload-graph -> Reload the current graph file
    • /add-edge -> Add edge to graph
    • /get-edge -> Get edge from graph
    • /add-node -> Add node to graph
    • /get-node -> Get node from graph
    • /find-path -> Find path between nodes in graph
  • Client wrapping server API calls
    • Graph info
    • Load graph
    • Reload graph
    • Add edge
    • Get edge
    • Add node
    • Get node
    • Find path
  • Auth?

Clients

Any HTTP client can be used to make requests to the server, such as fetch in the browser or curl on the command line. For example, fetch can be used to interact with a graph directly from a web app:

const response = await fetch('http://localhost:8000/graph-info')
const info = await response.json();

Dijkstar also includes a client that can be used to make requests conveniently from Python code:

from dijkstar.server.client import Client
client = Client()  # Uses the default base URL http://localhost:8000
info = client.graph_info()

This is intended for use in scripts, back end web services, and the like. Here's an example of using the client in a Django-style view:

def find_path_view(request):
    path = client.find_path(1, 2)
    # Process the path. For example, you might retrieve edges from
    # the database here.
    edges = Edge.objects.filter(id__in=path['edges'])
    edges = [{'id': edge.id, 'name': edge.name} for edge in edges]
    return JsonResponse(edges)

dijkstar's People

Contributors

elliotwutingfeng avatar wylee avatar xsetech avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

dijkstar's Issues

Add "cost to here" to predecessors and PathInfo?

Change predecessors[v] = (u, e, cost_of_e) to predecessors[v] = (u, e, cost_of_e, cost_of_s_to_u_plus_cost_of_e) in single_source_shortest_paths(), add a corresponding field to PathInfo, and update extract_shortest_path_from_predecessor_list().

I'm not sure off the top of my head what the use cases for this might be, but I saw that someone created a fork to replace cost_of_e with cost_of_s_to_u_plus_cost_of_e, so it's worth considering.

Add more/better examples to README

  • Add example of simple, non-dynamic costs (i.e., no cost function)
  • Modify the cost function example to show realistic usage of dynamic costs

Getting all nodes' shortest path cost

Since dijkstar algorithm can get a node to another node's shortest path and the node to all other nodes' shortest path. But the project only shows the shortest path from node u to node v, the remaining information of the shortest paths from node u to all other nodes can not be retrieved, some usage of this remaining information can be used! Can you add the interface of getting node u to all other nodes' shortest paths? I'm looking for your rely!

how to install server

hi , i want to install HTML server, but dont know how to install.
tried using command like this, we got error : WARNING: dijkstar 2.6.0 does not provide the extra 'server'

C:>pip install Dijkstar[server]
Requirement already satisfied: Dijkstar[server] in c:\program files\python\lib\site-packages (2.6.0)
WARNING: dijkstar 2.6.0 does not provide the extra 'server'
Requirement already satisfied: six in c:\program files\python\lib\site-packages (from Dijkstar[server]) (1.16.0)
C:>

thanks.

Allow nodes and edges to be tagged?

One use case for this would be to find nodes and edges with a given attribute value without having to iterate through all the nodes or edges. Possible API:

g = Graph()
g.add_edge(1, 2, tags={'type': 'highway'})
g.add_edge(2, 3, tags={'type': 'highway'})
highways = g.get_edges_by_tag(type='highway')

Internal tag storage might be a list:

def add_edge(self, u, v, edge=None, tags=None):
    ...
    if tags:
        self.tags.edges['type'].append(edge)

Cheapest Path Not Selected

When there are multiple paths of the same length but one is cheaper than the other, the algorithm does not return the cheapest path.

Why is this? I have tried modifying the cost function from the example, but that has not seemed to work.

Thanks.

Add support for undirected graphs?

It's currently possible to create an undirected graph by adding edges from both u to v and v to u for all connected pairs (u, v). This requires a bit of extra space and more effort to set up.

The simplest solution I can think of is to add an undirected flag to Graph. When enabled, (v, u) would be added automatically when (u, v) is added. This would still require extra space but wouldn't require any other changes (e.g., to the shortest path algorithm).

Doesn't work

graph = Graph()
graph.add_edge(1, 2, {'cost': 10})
graph.add_edge(1, 5, {'cost': 1})
graph.add_edge(1, 6, {'cost': 100})
graph.add_edge(2, 3, {'cost': 1})
graph.add_edge(3, 4, {'cost': 1})
graph.add_edge(3, 5, {'cost': 1})
graph.add_edge(3, 6, {'cost': 100})
graph.add_edge(3, 7, {'cost': 1})
graph.add_edge(4, 3, {'cost': 1})
graph.add_edge(4, 7, {'cost': 1})
graph.add_edge(5, 1, {'cost': 1})
graph.add_edge(5, 3, {'cost': 1})
graph.add_edge(5, 7, {'cost': 1})
graph.add_edge(6, 1, {'cost': 100})
graph.add_edge(6, 3, {'cost': 100})
graph.add_edge(7, 5, {'cost': 1})
graph.add_edge(7, 3, {'cost': 1})
graph.add_edge(7, 4, {'cost': 1})

I run Dijkstra, find BEST path, then remove node from SPT and try to find the SPT without this node. Regarding my topology there is path between 1-5-7-3-4, but this module cannot find this

PathInfo(nodes=[1, 5, 3, 4], edges=[{'cost': 1}, {'cost': 1}, {'cost': 1}], costs=[1, 1, 1], total_cost=3)
took 5 from queue [5, 3]
pop {5} and became PathInfo(nodes=[1, 2, 3, 4], edges=[{'cost': 10}, {'cost': 1}, {'cost': 1}], costs=[10, 1, 1], total_cost=12)
took 3 from queue [5, 3, 2]
no backup path without {3, 5}

Path Information

Hello,

I have created a simple graph by using a distance matrix.
Below you can find the code.

from dijkstar import Graph, find_path
matrix = ...... (distance matrix is created here)
graph = Graph()
for i in range(size):
    for j in range(size):
        graph.add_edge(i, j, {'cost': matrix[i][j]})        
cost_func = lambda u, v, e, prev_e: e['cost']
path = find_path(graph, 0, 6, cost_func=cost_func)
path

My question is, how am I able to learn the details of the founded path. There is a total cost, but I want to learn with which order of nodes I will get this cost. I want to learn the optimal order of nodes.

Thank you.

Graph algorhytm not working properly

Hello. I've just started to use this library, but it seems that it is not creating shortest path well.
Let me show my example:
Im trying to get from top left corner to bottom right corner.
I create a graph 100 x 100 and then connecting nodes, which are red.
I use PIL to analyze image to get all pixels that are red (route)

This image works good
https://www.dropbox.com/s/vasji38qxvzvamo/test_route_good.jpg?dl=0

But this is not!
https://www.dropbox.com/s/bvmup96t1ckbeuh/test_route_bad.jpg?dl=0

Here is my source code:
https://www.dropbox.com/s/ezpzkklun7ut0kz/dijkstra_not_working.zip?dl=0

Here is corresponding Stackovewflow question
https://stackoverflow.com/questions/50307453/deijkstar-shortest-path-not-working

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.