Skip to content

Commit

Permalink
Merge pull request #208 from TurtIeSocks/sorting-bootstrap
Browse files Browse the repository at this point in the history
refactor: bootstrapping
  • Loading branch information
TurtIeSocks authored Dec 2, 2023
2 parents 2ac65b9 + 513903a commit 3421925
Show file tree
Hide file tree
Showing 30 changed files with 1,303 additions and 840 deletions.
2 changes: 1 addition & 1 deletion client/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"private": true,
"name": "koji",
"version": "1.3.10",
"version": "1.3.11",
"description": "Tool to make RDM routes",
"main": "server/dist/index.js",
"author": "TurtIeSocks <[email protected]>",
Expand Down
3 changes: 2 additions & 1 deletion client/src/assets/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,10 +142,11 @@ export const PROPERTY_CATEGORIES = [
] as const

export const S2_CELL_LEVELS = [
// 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
] as const

export const BOOTSTRAP_LEVELS = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] as const
export const BOOTSTRAP_LEVELS = [1, 3, 5, 7, 9] as const

export const KEYBOARD_SHORTCUTS = [
{
Expand Down
46 changes: 29 additions & 17 deletions client/src/components/Loading.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,11 @@ import { usePersist } from '@hooks/usePersist'
import { useStatic } from '@hooks/useStatic'
import { fromSnakeCase } from '@services/utils'

export default function Loading() {
function TimerComponent() {
const loading = useStatic((s) => s.loading)
const loadingAbort = useStatic((s) => s.loadingAbort)
const totalStartTime = useStatic((s) => s.totalStartTime)
const totalLoadingTime = useStatic((s) => s.totalLoadingTime)
const setStatic = useStatic((s) => s.setStatic)

const loadingScreen = usePersist((s) => s.loadingScreen)
const settings = usePersist((s) => ({
mode: s.mode,
radius: s.radius,
category: s.category,
min_points: s.min_points,
fast: s.fast,
route_split_level: s.route_split_level,
}))
const [time, setTime] = React.useState(0)

const loadingStarted = Object.keys(loading).length
Expand All @@ -49,6 +38,33 @@ export default function Loading() {
}
}, [loadingStarted, totalLoadingTime])

return (
<Typography variant="h3" color="secondary">
{totalLoadingTime
? 'Stats'
: `Loading... ${loadingStatus.toFixed(2)}% | ${time.toFixed(1)}s`}
</Typography>
)
}

export default function Loading() {
const loading = useStatic((s) => s.loading)
const loadingAbort = useStatic((s) => s.loadingAbort)
const totalLoadingTime = useStatic((s) => s.totalLoadingTime)
const setStatic = useStatic((s) => s.setStatic)

const loadingScreen = usePersist((s) => s.loadingScreen)
const settings = usePersist((s) => ({
mode: s.mode,
radius: s.radius,
category: s.category,
min_points: s.min_points,
fast: s.fast,
route_split_level: s.route_split_level,
}))

const loadingStarted = Object.keys(loading).length

if (!loadingScreen) return null

return loadingStarted ? (
Expand All @@ -70,11 +86,7 @@ export default function Loading() {
}}
>
<Grid2 xs={12} sm={totalLoadingTime ? 4 : 12}>
<Typography variant="h3" color="secondary">
{totalLoadingTime
? 'Stats'
: `Loading... ${loadingStatus.toFixed(2)}% | ${time.toFixed(1)}s`}
</Typography>
<TimerComponent />
</Grid2>
<Grid2
xs={12}
Expand Down
17 changes: 5 additions & 12 deletions client/src/components/drawer/Routing.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,18 +104,11 @@ export default function RoutingTab() {
</Collapse>
</Collapse>

<Collapse in={mode === 'cluster'}>
<Divider sx={{ my: 2 }} />
<ListSubheader>Routing</ListSubheader>
<MultiOptionList
field="sort_by"
buttons={SORT_BY}
disabled={mode !== 'cluster'}
type="select"
/>
<Collapse in={sort_by === 'TSP'}>
<NumInput field="route_split_level" min={1} max={12} />
</Collapse>
<Divider sx={{ my: 2 }} />
<ListSubheader>Routing</ListSubheader>
<MultiOptionList field="sort_by" buttons={SORT_BY} type="select" />
<Collapse in={sort_by === 'TSP'}>
<NumInput field="route_split_level" min={1} max={12} />
</Collapse>

<Divider sx={{ my: 2 }} />
Expand Down
13 changes: 8 additions & 5 deletions client/src/pages/map/popups/Point.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -189,26 +189,29 @@ export function PointPopup({ id, lat, lon, type: geoType, dbRef }: Props) {
disabled={loading}
onClick={async () => {
setLoading(true)
const { fast, route_split_level, save_to_db } =
const { route_split_level, save_to_scanner, save_to_db, sort_by } =
usePersist.getState()
const { setStatic } = useStatic.getState()
setStatic('loading', { [name]: null })
setStatic('totalLoadingTime', 0)
setStatic('totalStartTime', Date.now())
const start = Date.now()
await fetchWrapper<KojiResponse<Feature>>(`/api/v1/calc/reroute`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
data_points: Object.values(useShapes.getState().Point).map(
(p) => [p.geometry.coordinates[1], p.geometry.coordinates[0]],
),
clusters: Object.values(useShapes.getState().Point).map((p) => [
p.geometry.coordinates[1],
p.geometry.coordinates[0],
]),
return_type: 'feature',
fast,
instance: name,
mode,
route_split_level,
save_to_scanner,
sort_by,
}),
}).then((res) => {
if (res) {
Expand Down
61 changes: 57 additions & 4 deletions or-tools/tsp/tsp.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <vector>
#include <iostream>
#include <string>
#include <thread>

#include "ortools/constraint_solver/routing.h"
#include "ortools/constraint_solver/routing_enums.pb.h"
Expand All @@ -26,6 +27,11 @@ namespace operations_research
const RoutingIndexManager::NodeIndex depot{0};
};

//! @brief Computes the distance between two nodes using the Haversine formula.
//! @param[in] lat1 Latitude of the first node.
//! @param[in] lon1 Longitude of the first node.
//! @param[in] lat2 Latitude of the second node.
//! @param[in] lon2 Longitude of the second node.
double haversine(double lat1, double lon1, double lat2, double lon2)
{

Expand All @@ -39,22 +45,57 @@ namespace operations_research
return R * c * 1000; // to reduce rounding issues
}

DistanceMatrix distanceMatrix(const RawInput &locations)
//! @brief Computes the distance matrix between all nodes.
//! @param[in] locations The locations of the nodes.
//! @param[out] distances The distance matrix between all nodes.
//! @param[in] start The index of the first node to compute.
//! @param[in] end The index of the last node to compute.
void computeDistances(const RawInput &locations, DistanceMatrix &distances, int start, int end)
{
DistanceMatrix distances = DistanceMatrix(locations.size(), std::vector<int64_t>(locations.size(), int64_t{0}));
for (int fromNode = 0; fromNode < locations.size(); fromNode++)
for (int fromNode = start; fromNode < end; ++fromNode)
{
for (int toNode = 0; toNode < locations.size(); toNode++)
for (int toNode = 0; toNode < locations.size(); ++toNode)
{
if (fromNode != toNode)
{
distances[fromNode][toNode] = static_cast<int64_t>(
haversine(locations[toNode][0], locations[toNode][1],
locations[fromNode][0], locations[fromNode][1]));
}
}
}
}

//! @brief Computes the distance matrix between all nodes.
//! @param[in] locations The [Lat, Lng] pairs.
DistanceMatrix distanceMatrix(const RawInput &locations)
{
auto start = std::chrono::high_resolution_clock::now();

int numThreads = std::thread::hardware_concurrency();

std::vector<std::thread> threads(numThreads);
DistanceMatrix distances = DistanceMatrix(locations.size(), std::vector<int64_t>(locations.size(), int64_t{0}));

int chunkSize = locations.size() / numThreads;
for (int i = 0; i < numThreads; ++i)
{
int start = i * chunkSize;
int end = (i == numThreads - 1) ? locations.size() : start + chunkSize;
threads[i] = std::thread(computeDistances, std::ref(locations), std::ref(distances), start, end);
}

for (auto &thread : threads)
{
thread.join();
}
return distances;
}

//! @brief Returns the routes of the solution.
//! @param[in] manager The manager of the routing problem.
//! @param[in] routing The routing model.
//! @param[in] solution The solution of the routing problem.
RawInput GetRoutes(const RoutingIndexManager &manager, const RoutingModel &routing, const Assignment &solution)
{
RawInput routes(manager.num_vehicles());
Expand All @@ -71,6 +112,8 @@ namespace operations_research
return routes;
}

//! @brief Solves the TSP problem.
//! @param[in] locations The [Lat, Lng] pairs.
RawInput Tsp(RawInput locations)
{
DataModel data;
Expand All @@ -93,6 +136,16 @@ namespace operations_research
searchParameters.set_first_solution_strategy(
FirstSolutionStrategy::PATH_CHEAPEST_ARC);

if (locations.size() > 1000)
{
searchParameters.set_local_search_metaheuristic(
LocalSearchMetaheuristic::GUIDED_LOCAL_SEARCH);
int64_t time = std::max(std::min(pow(locations.size() / 1000, 2.75), 3600.0), 3.0);
searchParameters.mutable_time_limit()->set_seconds(time);
// LOG(INFO) << "Time limit: " << time;
}
// searchParameters.set_log_search(true);

const Assignment *solution = routing.SolveWithParameters(searchParameters);

return GetRoutes(manager, routing, *solution);
Expand Down
6 changes: 3 additions & 3 deletions server/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion server/algorithms/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "algorithms"
version = "1.2.0"
version = "1.2.1"
edition = "2021"
publish = false

Expand Down
43 changes: 43 additions & 0 deletions server/algorithms/src/bootstrap/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use geojson::{Feature, FeatureCollection};
use model::api::{
args::{CalculationMode, SortBy},
Precision,
};

use crate::stats::Stats;

pub mod radius;
pub mod s2;

pub fn main(
area: FeatureCollection,
calculation_mode: CalculationMode,
radius: Precision,
sort_by: SortBy,
s2_level: u8,
s2_size: u8,
route_split_level: u64,
stats: &mut Stats,
) -> Vec<Feature> {
let mut features = vec![];

for feature in area.features {
match calculation_mode {
CalculationMode::Radius => {
let mut new_radius = radius::BootstrapRadius::new(&feature, radius);
new_radius.sort(&sort_by, route_split_level);

*stats += &new_radius.stats;
features.push(new_radius.feature());
}
CalculationMode::S2 => {
let mut new_s2 = s2::BootstrapS2::new(&feature, s2_level as u64, s2_size);
new_s2.sort(&sort_by, route_split_level);

*stats += &new_s2.stats;
features.push(new_s2.feature());
}
}
}
features
}
Loading

1 comment on commit 3421925

@vercel
Copy link

@vercel vercel bot commented on 3421925 Dec 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

koji – ./

koji-turtiesocks.vercel.app
koji-git-main-turtiesocks.vercel.app
koji.vercel.app

Please sign in to comment.