Releases: LdDl/ch
v1.10.0 - ManyToMany queries: thread safe pool and other optimizations
v1.9.0 - Recustomization
What's new
-
Added a way to dynamically rebuild CH when update specific edge's weight. Inspired by Customizable Contraction Hierarchies (Dibbelt, Strasser, Wagner) paper, but not using nested dissection, just vertices importance-based ordering.
This feature allows you to update edge weights without rebuilding the entire contraction hierarchy. Useful for:- Traffic updates (congestion, accidents and other events)
- Time-dependent routing
g := Graph{} graphFromCSV(&g, "data/pgrouting_osm.csv") g.PrepareContractionHierarchies() // Single update with immediate recustomization err := g.UpdateEdgeWeight(fromVertex, toVertex, newWeight, true) // Batch updates (more efficient for multiple changes) g.UpdateEdgeWeight(edge1From, edge1To, weight1, false) g.UpdateEdgeWeight(edge2From, edge2To, weight2, false) g.UpdateEdgeWeight(edge3From, edge3To, weight3, false) g.Recustomize() // Apply all changes at once
When to use single vs batch updates:
Scenario Method Why One edge changed UpdateEdgeWeight(..., true)Simple, immediate Multiple edges changed Batch + Recustomize()Faster, single pass Real-time traffic feed Batch + periodic Recustomize()Amortize cost If you have your own import logic (e.g., reading additional data like GeoJSON coordinates alongside the graph), you need to call
FinalizeImport()after loading all vertices, edges, and shortcuts:graph := ch.NewGraph() // Your custom import logic: // - CreateVertex() for each vertex // - AddEdge() for each edge // - SetOrderPos() and SetImportance() for each vertex // - AddShortcut() for each shortcut graph.FinalizeImport() // Required for recustomization support // Now graph is ready for queries and UpdateEdgeWeight/Recustomize
This is required because
FinalizeImport()builds internal data structures (contractionOrder,shortcutsByVia) needed for dynamic edge weight updates.If you use the built-in
ImportFromFile()function, this is called automatically.
For the more info see #48
v1.8.0 - Thread safe pool and other optimizations
What's new
- Thread-safe API via sync.Pool
- Optimizations via caching and epoch-based cleaning
For more information see #43
Breaking changes:
- In #47: Default Graph.ShortestPath() and related methods are no longer thread-safe due to epoch-based buffer reuse. If you need concurrent queries from multiple goroutines, use the new QueryPool API:
pool := graph.NewQueryPool() cost, path := pool.ShortestPath(source, target) costs, paths := pool.ShortestPathOneToMany(source, targets)
v1.7.8 - Fix ManyToMany
What's new
- Replaced rounding in tests for comparing floats to epsilon threshold
- ManyToMany was broken due map[] lookup error. See the ref.: 2d61de4
v1.7.7 - Minor
What's new
Minor improvements of code.
v1.7.6 - Fix import
What's new
- There were not closing file. So fix this bug - 9413bf1#diff-5a3040133bc8fe1d1df5b1e886d71838a22430b148df2e80c71bdbc741e52be1
- Little naming changes
v1.7.5 - Update export functions
What's new
Separate code of ExportToFile() in different blocks:
- ExportEdgesToFile
- ExportVerticesToFile
- ExportShortcutsToFile
There is a little overhead due the loop repeats, but 3-d party packages now can export only needed data (especially shortcuts)
v1.7.4 - Update shortcut getter
What's new
- Update method graph.IsShortcut.
Old:
// IsShortcut Returns true if edge is a shortcut (edge defined as two vertices)
func (graph *Graph) IsShortcut(labelFromVertex, labelToVertex int64) (ok bool) {
_, ok = graph.shortcuts[labelFromVertex][labelToVertex]
return ok
}New - Now maps proper source/target IDs + returns ViaVertex ID:
// IsShortcut Returns (vertex_id; true) if edge is a shortcut (edge defined as two vertices)
//
// If source or taget vertex is not found then returns (-1; false)
// If edge is not a shortcut then returns (-1; false)
//
func (graph *Graph) IsShortcut(labelFromVertex, labelToVertex int64) (int64, bool) {
source, ok := graph.mapping[labelFromVertex]
if !ok {
return -1, ok
}
target, ok := graph.mapping[labelToVertex]
if !ok {
return -1, ok
}
shortcut, ok := graph.shortcuts[source][target]
if !ok {
return -1, ok
}
return graph.Vertices[shortcut.ViaVertex].Label, ok
}v1.7.3 - New getters
What's new
- Added new method graph.IsShortcut.
// IsShortcut Returns true if edge is a shortcut (edge defined as two vertices)
func (graph *Graph) IsShortcut(labelFromVertex, labelToVertex int64) (ok bool) {
_, ok = graph.shortcuts[labelFromVertex][labelToVertex]
return ok
}- Added new method graph.FindVertex
// FindVertex Returns index of vertex in graph
//
// labelExternal - User defined ID of vertex
// If vertex is not found then returns (-1; false)
//
func (graph *Graph) FindVertex(labelExternal int64) (idx int64, ok bool) {
idx, ok = graph.mapping[labelExternal]
if !ok {
return -1, ok
}
return
}v1.7.2 - Eliminate shortcuts duplicates
What's new
- Added usefull methods on *Vertex such as: delete / update incident edge.
- Eliminated shortcut duplicates, e.g.:
A -> B -> C on some iteration gives shortcut cost 205.
A -> G -> C on some iteration gives shortcut cost less 195.
So we should eliminate A-B-C and create A-G-C