2022-06-14
This commit is contained in:
		
							parent
							
								
									dc68cce9ed
								
							
						
					
					
						commit
						652609ca71
					
				
					 18 changed files with 565 additions and 2144 deletions
				
			
		
							
								
								
									
										3
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							|  | @ -1,5 +1,6 @@ | ||||||
| rust/target | rust/target | ||||||
| rust/.history/ | rust/.history/ | ||||||
|  | rust/Cargo.lock | ||||||
| **/*.rs.bk | **/*.rs.bk | ||||||
| *.tmp | *.tmp | ||||||
| *.idx | *.idx | ||||||
|  | @ -20,7 +21,7 @@ pip-wheel-metadata | ||||||
| .eggs/ | .eggs/ | ||||||
| dist/ | dist/ | ||||||
| installer/Output/ | installer/Output/ | ||||||
| workspace.code-workspace   | workspace.code-workspace | ||||||
| ed_lrr_gui/web/jobs.db | ed_lrr_gui/web/jobs.db | ||||||
| ed_lrr_gui/web/ed_lrr_web_ui.db | ed_lrr_gui/web/ed_lrr_web_ui.db | ||||||
| __version__.py | __version__.py | ||||||
|  |  | ||||||
|  | @ -1,4 +1,11 @@ | ||||||
| include rust/Cargo.toml | include rust/Cargo.toml | ||||||
| include rust/.cargo/config | include rust/.cargo/config | ||||||
|  | exclude docs_mdbook | ||||||
|  | exclude celery_test | ||||||
|  | exclude installer | ||||||
|  | exclude imgui_test | ||||||
|  | exclude icon | ||||||
| recursive-include rust/src * | recursive-include rust/src * | ||||||
| recursive-include ed_lrr_gui * | recursive-include ed_lrr_gui * | ||||||
|  | recursive-exclude __pycache__ *.pyc *.pyo | ||||||
|  | global-exclude __pycache__ | ||||||
|  | @ -1,3 +1,9 @@ | ||||||
| [build-system] | [build-system] | ||||||
| requires = ["setuptools", "wheel","setuptools_rust"] | requires = ["setuptools", "wheel","setuptools_rust"] | ||||||
| build-backend = "setuptools.build_meta" | build-backend = "setuptools.build_meta" | ||||||
|  | 
 | ||||||
|  | [tool.poetry] | ||||||
|  | description = "Elite: Dangerous Long Range Route Plotter" | ||||||
|  | name="ed_lrr" | ||||||
|  | version="0.2.0" | ||||||
|  | authors = ["Daniel Seiller <earthnuker@gmail.com>"] | ||||||
							
								
								
									
										26
									
								
								rust/.devcontainer/devcontainer.json
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								rust/.devcontainer/devcontainer.json
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,26 @@ | ||||||
|  | // For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: | ||||||
|  | // https://github.com/microsoft/vscode-dev-containers/tree/v0.238.0/containers/docker-existing-dockerfile | ||||||
|  | { | ||||||
|  | 	"name": "ED_LRR", | ||||||
|  | 
 | ||||||
|  | 	// Sets the run context to one level up instead of the .devcontainer folder. | ||||||
|  | 	"context": "..", | ||||||
|  | 
 | ||||||
|  | 	// Update the 'dockerFile' property if you aren't using the standard 'Dockerfile' filename. | ||||||
|  | 	"dockerFile": "../Dockerfile", | ||||||
|  | 
 | ||||||
|  | 	// Use 'forwardPorts' to make a list of ports inside the container available locally. | ||||||
|  | 	// "forwardPorts": [], | ||||||
|  | 
 | ||||||
|  | 	// Uncomment the next line to run commands after the container is created - for example installing curl. | ||||||
|  | 	// "postCreateCommand": "apt-get update && apt-get install -y curl", | ||||||
|  | 
 | ||||||
|  | 	// Uncomment when using a ptrace-based debugger like C++, Go, and Rust | ||||||
|  | 	"runArgs": [ "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined" ] | ||||||
|  | 
 | ||||||
|  | 	// Uncomment to use the Docker CLI from inside the container. See https://aka.ms/vscode-remote/samples/docker-from-docker. | ||||||
|  | 	// "mounts": [ "source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind" ], | ||||||
|  | 
 | ||||||
|  | 	// Uncomment to connect as a non-root user if you've added one. See https://aka.ms/vscode-remote/containers/non-root. | ||||||
|  | 	// "remoteUser": "vscode" | ||||||
|  | } | ||||||
							
								
								
									
										5
									
								
								rust/.vscode/settings.json
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								rust/.vscode/settings.json
									
										
									
									
										vendored
									
									
								
							|  | @ -10,10 +10,11 @@ | ||||||
|     ], |     ], | ||||||
|     "discord.enabled": true, |     "discord.enabled": true, | ||||||
|     "python.pythonPath": "..\\.nox\\devenv-3-8\\python.exe", |     "python.pythonPath": "..\\.nox\\devenv-3-8\\python.exe", | ||||||
|     "jupyter.jupyterServerType": "remote", |     "jupyter.jupyterServerType": "local", | ||||||
|     "files.associations": { |     "files.associations": { | ||||||
|         "*.ksy": "yaml", |         "*.ksy": "yaml", | ||||||
|         "*.vpy": "python", |         "*.vpy": "python", | ||||||
|         "stat.h": "c" |         "stat.h": "c" | ||||||
|     } |     }, | ||||||
|  |     "rust-analyzer.diagnostics.disabled": ["unresolved-import"] | ||||||
| } | } | ||||||
							
								
								
									
										1718
									
								
								rust/Cargo.lock
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										1718
									
								
								rust/Cargo.lock
									
										
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -18,59 +18,58 @@ lto = "fat" | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| [dependencies] | [dependencies] | ||||||
| pyo3 = { version = "0.15.1", features = ["extension-module","eyre"] } | pyo3 = { version = "0.16.5", features = ["extension-module","eyre","abi3-py37"] } | ||||||
| csv = "1.1.6" | csv = "1.1.6" | ||||||
| humantime = "2.1.0" | humantime = "2.1.0" | ||||||
| permutohedron = "0.2.4" | permutohedron = "0.2.4" | ||||||
| serde_json = "1.0.74" | serde_json = "1.0.81" | ||||||
| fnv = "1.0.7" | fnv = "1.0.7" | ||||||
| bincode = "1.3.3" | bincode = "1.3.3" | ||||||
| sha3 = "0.10.0" | sha3 = "0.10.1" | ||||||
| byteorder = "1.4.3" | byteorder = "1.4.3" | ||||||
| rstar = "0.9.2" | rstar =  "0.9.3" | ||||||
| crossbeam-channel = "0.5.2" | crossbeam-channel = "0.5.4" | ||||||
| better-panic = "0.3.0" | better-panic = "0.3.0" | ||||||
| derivative = "2.2.0" | derivative = "2.2.0" | ||||||
| dict_derive = "0.4.0" | dict_derive = "0.4.0" | ||||||
| regex = "1.5.4" | regex = "1.5.6" | ||||||
| num_cpus = "1.13.1" | num_cpus = "1.13.1" | ||||||
| eddie = "0.4.2" | eddie = "0.4.2" | ||||||
| thiserror = "1.0.30" | thiserror = "1.0.31" | ||||||
| pyo3-log = "0.5.0" | pyo3-log = "0.6.0" | ||||||
| log = "0.4.14" | log = "0.4.17" | ||||||
| flate2 = "1.0.22" | flate2 = "1.0.24" | ||||||
| eval = "0.4.3" | eval = "0.4.3" | ||||||
| pythonize = "0.15.0" | pythonize = "0.16.0" | ||||||
| itertools = "0.10.3" | itertools = "0.10.3" | ||||||
| intmap = "0.7.1" |  | ||||||
| diff-struct = "0.4.1" |  | ||||||
| rustc-hash = "1.1.0" | rustc-hash = "1.1.0" | ||||||
| stats_alloc = "0.1.8" | stats_alloc = "0.1.10" | ||||||
| 
 | 
 | ||||||
| tracing = { version = "0.1.29", optional = true } | tracing = { version = "0.1.34", optional = true } | ||||||
| tracing-subscriber = { version = "0.3.5", optional = true } | tracing-subscriber = { version = "0.3.11", optional = true } | ||||||
| tracing-tracy = { version = "0.8.0", optional = true } | tracing-tracy = { version = "0.10.0", optional = true } | ||||||
| tracing-unwrap = { version = "0.9.2", optional = true } | # tracing-unwrap = { version = "0.9.2", optional = true } | ||||||
| tracy-client = { version = "0.12.6", optional = true } | tracy-client = { version = "0.14.0", optional = true } | ||||||
| tracing-chrome = "0.4.0" | tracing-chrome = "0.6.0" | ||||||
| rand = "0.8.4" | rand = "0.8.5" | ||||||
| eyre = "0.6.6" | eyre = "0.6.8" | ||||||
| memmap = "0.7.0" | memmap = "0.7.0" | ||||||
| csv-core = "0.1.10" | csv-core = "0.1.10" | ||||||
| postcard = { version = "0.7.3", features = ["alloc"] } |  | ||||||
| nohash-hasher = "0.2.0" | nohash-hasher = "0.2.0" | ||||||
|  | dashmap = "5.3.4" | ||||||
|  | rayon = "1.5.3" | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| [features] | [features] | ||||||
| profiling = ["tracing","tracing-subscriber","tracing-tracy","tracing-unwrap","tracy-client"] | profiling = ["tracing","tracing-subscriber","tracing-tracy","tracy-client"] | ||||||
| 
 | 
 | ||||||
| [dev-dependencies] | [dev-dependencies] | ||||||
| criterion = { version = "0.3.5", features = ["real_blackbox"] } | criterion = { version = "0.3.5", features = ["real_blackbox"] } | ||||||
| rand = "0.8.4" | rand = "0.8.5" | ||||||
| rand_distr = "0.4.2" | rand_distr = "0.4.3" | ||||||
| 
 | 
 | ||||||
| [dependencies.serde] | [dependencies.serde] | ||||||
| version = "1.0.133" | version = "1.0.137" | ||||||
| features = ["derive"] | features = ["derive"] | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										5
									
								
								rust/Dockerfile
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								rust/Dockerfile
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,5 @@ | ||||||
|  | FROM ghcr.io/pyo3/maturin | ||||||
|  | 
 | ||||||
|  | LABEL ed_lrr_dev latest | ||||||
|  | RUN rustup default nightly | ||||||
|  | RUN pip install maturin[zig] | ||||||
							
								
								
									
										1
									
								
								rust/clippy.toml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								rust/clippy.toml
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | disallowed-types = ["std::collections::HashMap", "std::collections::HashSet"] | ||||||
							
								
								
									
										8
									
								
								rust/docker-compose.yml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								rust/docker-compose.yml
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,8 @@ | ||||||
|  | version: "4.0" | ||||||
|  | services: | ||||||
|  |   ed_lrr_build: | ||||||
|  |     build: . | ||||||
|  |     working_dir: /code | ||||||
|  |     command: ["build","-r","--zig", "--compatibility","manylinux2010"] | ||||||
|  |     volumes: | ||||||
|  |       - .:/code | ||||||
|  | @ -6,32 +6,46 @@ import shutil | ||||||
| import json | import json | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def setup_logging(loglevel="INFO"): | def setup_logging(loglevel="INFO", file=False): | ||||||
|     import logging |     import logging | ||||||
|     import coloredlogs |     import coloredlogs | ||||||
| 
 | 
 | ||||||
|     coloredlogs.DEFAULT_FIELD_STYLES["delta"] = {"color": "green"} |     logfmt = " | ".join( | ||||||
|     coloredlogs.DEFAULT_FIELD_STYLES["levelname"] = {"color": "yellow"} |         ["[%(delta)s] %(levelname)s", "%(name)s:%(pathname)s:%(lineno)s", "%(message)s"] | ||||||
|  |     ) | ||||||
| 
 | 
 | ||||||
|     class DeltaTimeFormatter(coloredlogs.ColoredFormatter): |     class ColorDeltaTimeFormatter(coloredlogs.ColoredFormatter): | ||||||
|         def format(self, record): |         def format(self, record): | ||||||
|             seconds = record.relativeCreated / 1000 |             seconds = record.relativeCreated / 1000 | ||||||
|             duration = timedelta(seconds=seconds) |             duration = timedelta(seconds=seconds) | ||||||
|             record.delta = str(duration) |             record.delta = str(duration) | ||||||
|             return super().format(record) |             return super().format(record) | ||||||
| 
 | 
 | ||||||
|     coloredlogs.ColoredFormatter = DeltaTimeFormatter |     class DeltaTimeFormatter(coloredlogs.BasicFormatter): | ||||||
|     logfmt = " | ".join( |         def format(self, record): | ||||||
|         ["[%(delta)s] %(levelname)s", "%(name)s:%(pathname)s:%(lineno)s", "%(message)s"] |             seconds = record.relativeCreated / 1000 | ||||||
|     ) |             duration = timedelta(seconds=seconds) | ||||||
|  |             record.delta = str(duration) | ||||||
|  |             return super().format(record) | ||||||
|  | 
 | ||||||
|  |     logger = logging.getLogger() | ||||||
|  |     if file: | ||||||
|  |         open("ed_lrr_test.log", "w").close() | ||||||
|  |         fh = logging.FileHandler("ed_lrr_test.log") | ||||||
|  |         fh.setLevel(logging.DEBUG) | ||||||
|  |         fh.setFormatter(DeltaTimeFormatter(logfmt)) | ||||||
|  |         logger.addHandler(fh) | ||||||
|  |     coloredlogs.DEFAULT_FIELD_STYLES["delta"] = {"color": "green"} | ||||||
|  |     coloredlogs.DEFAULT_FIELD_STYLES["levelname"] = {"color": "yellow"} | ||||||
|  | 
 | ||||||
|  |     coloredlogs.ColoredFormatter = ColorDeltaTimeFormatter | ||||||
|     numeric_level = getattr(logging, loglevel.upper(), None) |     numeric_level = getattr(logging, loglevel.upper(), None) | ||||||
|     if not isinstance(numeric_level, int): |     if not isinstance(numeric_level, int): | ||||||
|         raise ValueError("Invalid log level: %s" % loglevel) |         raise ValueError("Invalid log level: %s" % loglevel) | ||||||
|     coloredlogs.install(level=numeric_level, fmt=logfmt) |     coloredlogs.install(level=numeric_level, fmt=logfmt, logger=logger) | ||||||
|  |     return | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| setup_logging() |  | ||||||
| 
 |  | ||||||
| JUMP_RANGE = 48 | JUMP_RANGE = 48 | ||||||
| globals().setdefault("__file__", r"D:\devel\rust\ed_lrr_gui\rust\run_test.py") | globals().setdefault("__file__", r"D:\devel\rust\ed_lrr_gui\rust\run_test.py") | ||||||
| dirname = os.path.dirname(__file__) or "." | dirname = os.path.dirname(__file__) or "." | ||||||
|  | @ -39,28 +53,38 @@ os.chdir(dirname) | ||||||
| t_start = datetime.now() | t_start = datetime.now() | ||||||
| os.environ["PYO3_PYTHON"] = sys.executable | os.environ["PYO3_PYTHON"] = sys.executable | ||||||
| if "--clean" in sys.argv[1:]: | if "--clean" in sys.argv[1:]: | ||||||
|     SP.check_call(["cargo","clean"]) |     SP.check_call(["cargo", "clean"]) | ||||||
| if "--build" in sys.argv[1:]: | if "--build" in sys.argv[1:]: | ||||||
|     SP.check_call(["cargo","lcheck"]) |     SP.check_call(["cargo", "lcheck"]) | ||||||
|     SP.check_call([sys.executable, "-m", "pip", "install", "-e", ".."]) |     SP.check_call([sys.executable, "-m", "pip", "install", "-e", ".."]) | ||||||
|     print("Build+Install took:", datetime.now() - t_start) |     print("Build+Install took:", datetime.now() - t_start) | ||||||
| 
 | 
 | ||||||
| sys.path.append("..") | sys.path.append("..") | ||||||
|  | setup_logging(file=True) | ||||||
| _ed_lrr = __import__("_ed_lrr") | _ed_lrr = __import__("_ed_lrr") | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| def callback(state): | def callback(state): | ||||||
|     print(state) |     print(state) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| print(_ed_lrr) | print(_ed_lrr) | ||||||
| r = _ed_lrr.PyRouter(callback) |  | ||||||
| r.load("../stars_2.csv", immediate=False) |  | ||||||
| print(r) |  | ||||||
| r.str_tree_test() |  | ||||||
| 
 |  | ||||||
| exit() |  | ||||||
| 
 |  | ||||||
| r = _ed_lrr.PyRouter(callback) | r = _ed_lrr.PyRouter(callback) | ||||||
| r.load("../stars.csv", immediate=False) | r.load("../stars.csv", immediate=False) | ||||||
| print(r.resolve("Sol","Saggitarius A","Colonia","Merope")) | # r.nb_perf_test(JUMP_RANGE) | ||||||
|  | # exit() | ||||||
|  | # start, end = "Sol", "Colonia" | ||||||
|  | # systems = r.resolve(start, end) | ||||||
|  | # sys_ids = {k: v["id"] for k, v in systems.items()} | ||||||
|  | 
 | ||||||
|  | r.bfs_test(JUMP_RANGE) | ||||||
|  | 
 | ||||||
|  | # cfg = {} | ||||||
|  | # cfg["mode"] = "incremental_broadening" | ||||||
|  | # # input("{}>".format(os.getpid())) | ||||||
|  | # # route = r.precompute_neighbors(JUMP_RANGE) | ||||||
|  | # route = r.route([sys_ids[start], sys_ids[end]], JUMP_RANGE, cfg, 0) | ||||||
|  | # print("Optimal route:", len(route)) | ||||||
| 
 | 
 | ||||||
| exit() | exit() | ||||||
| 
 | 
 | ||||||
|  | @ -68,10 +92,12 @@ ships = _ed_lrr.PyShip.from_journal() | ||||||
| r = _ed_lrr.PyRouter(callback) | r = _ed_lrr.PyRouter(callback) | ||||||
| r.load("../stars.csv", immediate=False) | r.load("../stars.csv", immediate=False) | ||||||
| 
 | 
 | ||||||
| def func(*args,**kwargs): | 
 | ||||||
|  | def func(*args, **kwargs): | ||||||
|     print(kwargs) |     print(kwargs) | ||||||
|     return 12 |     return 12 | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| r.precompute_neighbors(JUMP_RANGE) | r.precompute_neighbors(JUMP_RANGE) | ||||||
| 
 | 
 | ||||||
| exit() | exit() | ||||||
|  | @ -119,6 +145,7 @@ start, end = "Sol", "Colonia" | ||||||
| systems = r.resolve(start, end) | systems = r.resolve(start, end) | ||||||
| sys_ids = {k: v["id"] for k, v in systems.items()} | sys_ids = {k: v["id"] for k, v in systems.items()} | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| cfg = {} | cfg = {} | ||||||
| cfg["mode"] = "incremental_broadening" | cfg["mode"] = "incremental_broadening" | ||||||
| # input("{}>".format(os.getpid())) | # input("{}>".format(os.getpid())) | ||||||
|  |  | ||||||
							
								
								
									
										2
									
								
								rust/rust-toolchain.toml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								rust/rust-toolchain.toml
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,2 @@ | ||||||
|  | [toolchain] | ||||||
|  | channel = "nightly" | ||||||
|  | @ -1,11 +1,12 @@ | ||||||
| //! # Common utlility functions
 | //! # Common utlility functions
 | ||||||
| use crate::route::{LineCache, Router}; | use crate::route::Router; | ||||||
| use bincode::Options; | use bincode::Options; | ||||||
| use crossbeam_channel::{bounded, Receiver}; | use crossbeam_channel::{bounded, Receiver}; | ||||||
| use csv::ByteRecord; | use csv::ByteRecord; | ||||||
| use dict_derive::IntoPyObject; | use dict_derive::IntoPyObject; | ||||||
| use eyre::Result; | use eyre::Result; | ||||||
| use log::*; | use log::*; | ||||||
|  | use nohash_hasher::NoHashHasher; | ||||||
| use pyo3::prelude::*; | use pyo3::prelude::*; | ||||||
| use pyo3::types::PyDict; | use pyo3::types::PyDict; | ||||||
| use pyo3::{conversion::ToPyObject, create_exception}; | use pyo3::{conversion::ToPyObject, create_exception}; | ||||||
|  | @ -13,8 +14,7 @@ use pythonize::depythonize; | ||||||
| use serde::{Deserialize, Serialize}; | use serde::{Deserialize, Serialize}; | ||||||
| use sha3::{Digest, Sha3_256}; | use sha3::{Digest, Sha3_256}; | ||||||
| use std::collections::{BTreeMap, HashMap, HashSet, VecDeque}; | use std::collections::{BTreeMap, HashMap, HashSet, VecDeque}; | ||||||
| use std::hash::{Hash, Hasher, BuildHasherDefault}; | use std::hash::{BuildHasherDefault, Hash, Hasher}; | ||||||
| use std::io::Write; |  | ||||||
| use std::ops::{Deref, DerefMut}; | use std::ops::{Deref, DerefMut}; | ||||||
| use std::path::Path; | use std::path::Path; | ||||||
| use std::str::FromStr; | use std::str::FromStr; | ||||||
|  | @ -25,22 +25,21 @@ use std::{ | ||||||
|     io::{BufReader, BufWriter}, |     io::{BufReader, BufWriter}, | ||||||
|     path::PathBuf, |     path::PathBuf, | ||||||
| }; | }; | ||||||
| use nohash_hasher::NoHashHasher; |  | ||||||
| use thiserror::Error; | use thiserror::Error; | ||||||
| 
 | 
 | ||||||
| #[inline(always)] | #[inline(always)] | ||||||
| pub fn heuristic(range: f32, node: &TreeNode, goal: &TreeNode) -> f32 { | pub fn heuristic(range: f32, node: &TreeNode, goal: &TreeNode) -> f32 { | ||||||
|     // distance remaining after jumping from node towards goal
 |     // distance remaining after jumping from node towards goal
 | ||||||
|     let a2 = dist2(&node.pos, &goal.pos); |     let a2 = dist(&node.pos, &goal.pos); | ||||||
|     let mult=node.get_mult(); |     let mult = node.get_mult(); | ||||||
|     let b2 = range * range * mult*mult; |     let b2 = range * mult; | ||||||
|     return (a2 - b2).max(0.0); |     return (a2 - b2).max(0.0); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Min-heap priority queue using f32 as priority
 | /// Min-heap priority queue using f32 as priority
 | ||||||
| pub struct MinFHeap<T: Ord>(pub BinaryHeap<(Reverse<F32>, T)>); | pub struct MinFHeap<T: Ord>(BinaryHeap<(Reverse<F32>, T)>); | ||||||
| /// Max-heap priority queue using f32 as priority
 | /// Max-heap priority queue using f32 as priority
 | ||||||
| pub struct MaxFHeap<T: Ord>(pub BinaryHeap<(F32, T)>); | pub struct MaxFHeap<T: Ord>(BinaryHeap<(F32, T)>); | ||||||
| 
 | 
 | ||||||
| impl<T: Ord> MaxFHeap<T> { | impl<T: Ord> MaxFHeap<T> { | ||||||
|     /// Create new, empty priority queue
 |     /// Create new, empty priority queue
 | ||||||
|  | @ -391,7 +390,7 @@ pub enum SysEntry { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl ToPyObject for SysEntry { | impl ToPyObject for SysEntry { | ||||||
|     fn to_object(&self, py: Python) -> PyObject { |     fn to_object(&self, py: Python<'_>) -> PyObject { | ||||||
|         match self { |         match self { | ||||||
|             Self::ID(id) => id.to_object(py), |             Self::ID(id) => id.to_object(py), | ||||||
|             Self::Name(name) => name.to_object(py), |             Self::Name(name) => name.to_object(py), | ||||||
|  | @ -483,99 +482,6 @@ pub fn ndot(u: &[f32; 3], v: &[f32; 3]) -> f32 { | ||||||
|     (u[0] * v[0]) / lm + (u[1] * v[1]) / lm + (u[2] * v[2]) / lm |     (u[0] * v[0]) / lm + (u[1] * v[1]) / lm + (u[2] * v[2]) / lm | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Fuzzy string matcher, use to resolve star system names
 |  | ||||||
| #[cfg_attr(feature = "profiling", tracing::instrument(skip(rx)))] |  | ||||||
| fn matcher( |  | ||||||
|     rx: Receiver<ByteRecord>, |  | ||||||
|     names: Vec<String>, |  | ||||||
|     exact: bool, |  | ||||||
| ) -> HashMap<String, (f64, Option<u32>)> { |  | ||||||
|     let mut best: HashMap<String, (f64, Option<u32>)> = HashMap::new(); |  | ||||||
|     for name in &names { |  | ||||||
|         best.insert(name.to_string(), (0.0, None)); |  | ||||||
|     } |  | ||||||
|     let names_u8: Vec<(String, _)> = names.iter().map(|n| (n.clone(), n.as_bytes())).collect(); |  | ||||||
|     let sdist = eddie::slice::Levenshtein::new(); |  | ||||||
|     for sys in rx.into_iter() { |  | ||||||
|         for (name, name_b) in &names_u8 { |  | ||||||
|             if let Some(ent) = best.get_mut(name) { |  | ||||||
|                 if (ent.0 - 1.0).abs() < std::f64::EPSILON { |  | ||||||
|                     continue; |  | ||||||
|                 } |  | ||||||
|                 if exact && (&sys[1] == *name_b) { |  | ||||||
|                     let id = std::str::from_utf8(&sys[0]).unwrap().parse().unwrap(); |  | ||||||
|                     *ent = (1.0, Some(id)); |  | ||||||
|                     continue; |  | ||||||
|                 } |  | ||||||
|                 let d = sdist.similarity(&sys[1], name_b); |  | ||||||
|                 if d > ent.0 { |  | ||||||
|                     let id = std::str::from_utf8(&sys[0]).unwrap().parse().unwrap(); |  | ||||||
|                     *ent = (d, Some(id)); |  | ||||||
|                 } |  | ||||||
|             }; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     best |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /// Scan through the csv file at `path` and return a hash map
 |  | ||||||
| /// mapping the strings from `names` to a tuple `(score, Option<system_id>)`.
 |  | ||||||
| /// Scoring matching uses the normalized Levenshtein distance where 1.0 is an exact match.
 |  | ||||||
| pub fn find_matches( |  | ||||||
|     path: &Path, |  | ||||||
|     names: Vec<String>, |  | ||||||
|     exact: bool, |  | ||||||
| ) -> Result<HashMap<String, (f64, Option<u32>)>, String> { |  | ||||||
|     let mut best: HashMap<String, (f64, Option<u32>)> = HashMap::new(); |  | ||||||
|     if names.is_empty() { |  | ||||||
|         return Ok(best); |  | ||||||
|     } |  | ||||||
|     for name in &names { |  | ||||||
|         best.insert(name.to_string(), (0.0, None)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     let mut workers = Vec::new(); |  | ||||||
|     let ncpus = num_cpus::get(); |  | ||||||
|     let (tx, rx) = bounded(4096 * ncpus); |  | ||||||
|     for _ in 0..ncpus { |  | ||||||
|         let names = names.clone(); |  | ||||||
|         let rx = rx.clone(); |  | ||||||
|         let th = thread::spawn(move || matcher(rx, names, exact)); |  | ||||||
|         workers.push(th); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     let mut rdr = match csv::ReaderBuilder::new().has_headers(false).from_path(path) { |  | ||||||
|         Ok(rdr) => rdr, |  | ||||||
|         Err(e) => { |  | ||||||
|             return Err(format!("Error opening {}: {}", path.to_str().unwrap(), e)); |  | ||||||
|         } |  | ||||||
|     }; |  | ||||||
|     let t_start = std::time::Instant::now(); |  | ||||||
|     let mut processed: usize = 0; |  | ||||||
|     for record in rdr.byte_records().flat_map(|v| v.ok()) { |  | ||||||
|         tx.send(record).unwrap(); |  | ||||||
|         processed += 1; |  | ||||||
|     } |  | ||||||
|     drop(tx); |  | ||||||
|     while let Some(th) = workers.pop() { |  | ||||||
|         for (name, (score, sys)) in th.join().unwrap().iter() { |  | ||||||
|             best.entry(name.clone()).and_modify(|ent| { |  | ||||||
|                 if score > &ent.0 { |  | ||||||
|                     *ent = (*score, *sys); |  | ||||||
|                 } |  | ||||||
|             }); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     let dt = std::time::Instant::now() - t_start; |  | ||||||
|     info!( |  | ||||||
|         "Searched {} records in {:?}: {} records/second", |  | ||||||
|         processed, |  | ||||||
|         dt, |  | ||||||
|         (processed as f64) / dt.as_secs_f64() |  | ||||||
|     ); |  | ||||||
|     Ok(best) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /// Hash the contents of `path` with sha3 and return the hash as a vector of bytes
 | /// Hash the contents of `path` with sha3 and return the hash as a vector of bytes
 | ||||||
| fn hash_file(path: &Path) -> Vec<u8> { | fn hash_file(path: &Path) -> Vec<u8> { | ||||||
|     let mut hash_reader = BufReader::new(File::open(path).unwrap()); |     let mut hash_reader = BufReader::new(File::open(path).unwrap()); | ||||||
|  | @ -632,7 +538,7 @@ pub struct TreeNode { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl ToPyObject for TreeNode { | impl ToPyObject for TreeNode { | ||||||
|     fn to_object(&self, py: Python) -> PyObject { |     fn to_object(&self, py: Python<'_>) -> PyObject { | ||||||
|         pythonize::pythonize(py, self).unwrap() |         pythonize::pythonize(py, self).unwrap() | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -647,7 +553,7 @@ impl TreeNode { | ||||||
|         match self.flags { |         match self.flags { | ||||||
|             0b11 => 4.0, |             0b11 => 4.0, | ||||||
|             0b10 => 1.5, |             0b10 => 1.5, | ||||||
|             _ => 1.0 |             _ => 1.0, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -696,22 +602,21 @@ pub struct System { | ||||||
| 
 | 
 | ||||||
| impl System { | impl System { | ||||||
|     fn get_flags(&self) -> u8 { |     fn get_flags(&self) -> u8 { | ||||||
|         let mut flags=0; |         if self.mult == 4.0 { | ||||||
|         if self.mult==4.0 { |             return 0b11; | ||||||
|             return 0b11 |  | ||||||
|         } |         } | ||||||
|         if self.mult==1.5 { |         if self.mult == 1.5 { | ||||||
|             return 0b10 |             return 0b10; | ||||||
|         } |         } | ||||||
|         if self.has_scoopable { |         if self.has_scoopable { | ||||||
|             return 0b01 |             return 0b01; | ||||||
|         } |         } | ||||||
|         return 0b00 |         return 0b00; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl ToPyObject for System { | impl ToPyObject for System { | ||||||
|     fn to_object(&self, py: Python) -> PyObject { |     fn to_object(&self, py: Python<'_>) -> PyObject { | ||||||
|         let d = PyDict::new(py); |         let d = PyDict::new(py); | ||||||
|         d.set_item("id", self.id).unwrap(); |         d.set_item("id", self.id).unwrap(); | ||||||
|         d.set_item("name", self.name.clone()).unwrap(); |         d.set_item("name", self.name.clone()).unwrap(); | ||||||
|  | @ -771,51 +676,55 @@ impl<T> Default for DQueue<T> { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| #[derive(Debug, Default, Serialize, Deserialize)] | #[derive(Debug, Default, Serialize, Deserialize)] | ||||||
| struct BKTreeNode { | struct BKTreeNode { | ||||||
|     ids: HashSet<u32,BuildHasherDefault<NoHashHasher<u32>>>, |     ids: HashSet<u32, BuildHasherDefault<NoHashHasher<u32>>>, | ||||||
|     children: HashMap<u8,Self,BuildHasherDefault<NoHashHasher<u8>>> |     children: HashMap<u8, Self, BuildHasherDefault<NoHashHasher<u8>>>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl BKTreeNode { | impl BKTreeNode { | ||||||
|     fn new(data: &[String], dist: &eddie::str::Levenshtein) -> Self { |     fn new(data: &[String], dist: &eddie::str::Levenshtein) -> Self { | ||||||
|         let mut tree= Self::default(); |         let mut tree = Self::default(); | ||||||
|         let mut max_depth=0; |         let mut max_depth = 0; | ||||||
|         (0..data.len()).map(|id| { |         (0..data.len()) | ||||||
|             max_depth=max_depth.max(tree.insert(data,id as u32, dist,0)); |             .map(|id| { | ||||||
|             if (id>0) && (id%100_000 == 0) { |                 max_depth = max_depth.max(tree.insert(data, id as u32, dist, 0)); | ||||||
|                 println!("Inserting ID {}, Max Depth: {}",id,max_depth); |                 if (id > 0) && (id % 100_000 == 0) { | ||||||
|             } |                     println!("Inserting ID {}, Max Depth: {}", id, max_depth); | ||||||
|         }).max(); |                 } | ||||||
|         println!("Max Depth: {}",max_depth); |             }) | ||||||
|  |             .max(); | ||||||
|  |         println!("Max Depth: {}", max_depth); | ||||||
|         tree |         tree | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn from_id(id: u32) -> Self { |     fn from_id(id: u32) -> Self { | ||||||
|         let mut ret=Self::default(); |         let mut ret = Self::default(); | ||||||
|         ret.ids.insert(id); |         ret.ids.insert(id); | ||||||
|         return ret; |         return ret; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn insert(&mut self, data: &[String],id: u32, dist: &eddie::str::Levenshtein, depth: usize) -> usize { |     fn insert( | ||||||
|  |         &mut self, | ||||||
|  |         data: &[String], | ||||||
|  |         id: u32, | ||||||
|  |         dist: &eddie::str::Levenshtein, | ||||||
|  |         depth: usize, | ||||||
|  |     ) -> usize { | ||||||
|         if self.is_empty() { |         if self.is_empty() { | ||||||
|             self.ids.insert(id); |             self.ids.insert(id); | ||||||
|             return depth; |             return depth; | ||||||
|         } |         } | ||||||
|         let idx = self.get_id().unwrap() as usize; |         let idx = self.get_id().unwrap() as usize; | ||||||
|         let self_key = data.get(idx).unwrap(); |         let dist_key = dist.distance(&data[idx], &data[id as usize]) as u8; | ||||||
|         let ins_key = data.get(id as usize).unwrap(); |         if dist_key == 0 { | ||||||
|         let dist_key = dist.distance(self_key,ins_key) as u8; |  | ||||||
|         if dist_key==0 { |  | ||||||
|             self.ids.insert(id); |             self.ids.insert(id); | ||||||
|             return depth; |             return depth; | ||||||
|         } |         } | ||||||
|         if let Some(child) = self.children.get_mut(&dist_key) { |         if let Some(child) = self.children.get_mut(&dist_key) { | ||||||
|             return child.insert(data,id,dist,depth+1); |             return child.insert(data, id, dist, depth + 1); | ||||||
|         } else { |         } else { | ||||||
|             self.children.insert(dist_key,Self::from_id(id)); |             self.children.insert(dist_key, Self::from_id(id)); | ||||||
|             return depth; |             return depth; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | @ -835,13 +744,11 @@ pub struct BKTree { | ||||||
|     root: BKTreeNode, |     root: BKTreeNode, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| impl BKTree { | impl BKTree { | ||||||
|     pub fn new(data: &[String], base_id: u32) -> Self { |     pub fn new(data: &[String], base_id: u32) -> Self { | ||||||
|         let dist = eddie::str::Levenshtein::new(); |         let dist = eddie::str::Levenshtein::new(); | ||||||
|         let root = BKTreeNode::new(data, &dist); |         let root = BKTreeNode::new(data, &dist); | ||||||
|         Self {base_id,root} |         Self { base_id, root } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn id(&self) -> u32 { |     pub fn id(&self) -> u32 { | ||||||
|  | @ -851,8 +758,8 @@ impl BKTree { | ||||||
|     pub fn dump(&self, fh: &mut BufWriter<File>) -> EdLrrResult<()> { |     pub fn dump(&self, fh: &mut BufWriter<File>) -> EdLrrResult<()> { | ||||||
|         let options = bincode::DefaultOptions::new(); |         let options = bincode::DefaultOptions::new(); | ||||||
|         let amt = options.serialized_size(self)?; |         let amt = options.serialized_size(self)?; | ||||||
|         println!("Writing {}",amt); |         println!("Writing {}", amt); | ||||||
|         options.serialize_into(fh,self)?; |         options.serialize_into(fh, self)?; | ||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										196
									
								
								rust/src/lib.rs
									
										
									
									
									
								
							
							
						
						
									
										196
									
								
								rust/src/lib.rs
									
										
									
									
									
								
							|  | @ -1,4 +1,6 @@ | ||||||
|  | #![feature(binary_heap_retain)] | ||||||
| #![allow(dead_code, clippy::needless_return, clippy::too_many_arguments)] | #![allow(dead_code, clippy::needless_return, clippy::too_many_arguments)] | ||||||
|  | #![warn(rust_2018_idioms, rust_2021_compatibility, clippy::disallowed_types)] | ||||||
| //! # Elite: Danerous Long Range Router
 | //! # Elite: Danerous Long Range Router
 | ||||||
| pub mod common; | pub mod common; | ||||||
| pub mod galaxy; | pub mod galaxy; | ||||||
|  | @ -10,13 +12,9 @@ pub mod route; | ||||||
| pub mod search_algos; | pub mod search_algos; | ||||||
| pub mod ship; | pub mod ship; | ||||||
| 
 | 
 | ||||||
| use bincode::Options; |  | ||||||
| use csv::{Position, StringRecord}; |  | ||||||
| use eddie::Levenshtein; |  | ||||||
| // =========================
 | // =========================
 | ||||||
| use stats_alloc::{Region, StatsAlloc, INSTRUMENTED_SYSTEM}; | use stats_alloc::{Region, StatsAlloc, INSTRUMENTED_SYSTEM}; | ||||||
| use std::alloc::System as SystemAlloc; | use std::alloc::System as SystemAlloc; | ||||||
| use std::cell::RefMut; |  | ||||||
| use std::collections::BTreeMap; | use std::collections::BTreeMap; | ||||||
| use std::io::{BufWriter, Write}; | use std::io::{BufWriter, Write}; | ||||||
| use std::path::Path; | use std::path::Path; | ||||||
|  | @ -30,8 +28,7 @@ mod profiling { | ||||||
|     pub fn init() {} |     pub fn init() {} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| extern crate derivative; | use crate::common::{grid_stats, EdLrrError, SysEntry, System}; | ||||||
| use crate::common::{find_matches, grid_stats, EdLrrError, SysEntry, System}; |  | ||||||
| #[cfg(feature = "profiling")] | #[cfg(feature = "profiling")] | ||||||
| use crate::profiling::*; | use crate::profiling::*; | ||||||
| use crate::route::{Router, SearchState}; | use crate::route::{Router, SearchState}; | ||||||
|  | @ -39,16 +36,14 @@ use crate::ship::Ship; | ||||||
| use eyre::Result; | use eyre::Result; | ||||||
| #[cfg(not(feature = "profiling"))] | #[cfg(not(feature = "profiling"))] | ||||||
| use log::*; | use log::*; | ||||||
|  | use pyo3::create_exception; | ||||||
| use pyo3::exceptions::*; | use pyo3::exceptions::*; | ||||||
| use pyo3::prelude::*; | use pyo3::prelude::*; | ||||||
| use pyo3::types::{IntoPyDict, PyDict, PyTuple}; | use pyo3::types::{IntoPyDict, PyDict, PyTuple}; | ||||||
| use pyo3::{create_exception, PyObjectProtocol}; | use route::PyModeConfig; | ||||||
| use route::{LineCache, PyModeConfig}; | use std::{collections::HashMap, convert::TryInto, fs::File, path::PathBuf}; | ||||||
| use std::{ |  | ||||||
|     cell::RefCell, collections::HashMap, convert::TryInto, fs::File, io::BufReader, path::PathBuf, |  | ||||||
| }; |  | ||||||
| 
 | 
 | ||||||
| #[cfg(feature = "profiling")] | #[cfg(feature = "mem_profiling")] | ||||||
| #[global_allocator] | #[global_allocator] | ||||||
| static GLOBAL: ProfiledAllocator<std::alloc::System> = | static GLOBAL: ProfiledAllocator<std::alloc::System> = | ||||||
|     ProfiledAllocator::new(std::alloc::System, 1024); |     ProfiledAllocator::new(std::alloc::System, 1024); | ||||||
|  | @ -87,8 +82,6 @@ impl PyRouter { | ||||||
|             .ok_or_else(|| PyErr::from(EdLrrError::RuntimeError("no stars.csv loaded".to_owned()))) |             .ok_or_else(|| PyErr::from(EdLrrError::RuntimeError("no stars.csv loaded".to_owned()))) | ||||||
|             .map(PathBuf::from) |             .map(PathBuf::from) | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[pymethods] | #[pymethods] | ||||||
|  | @ -115,25 +108,23 @@ impl PyRouter { | ||||||
| 
 | 
 | ||||||
|     #[args(primary_only = "false", immediate = "false")] |     #[args(primary_only = "false", immediate = "false")] | ||||||
|     #[pyo3(text_signature = "(path, primary_only, /)")] |     #[pyo3(text_signature = "(path, primary_only, /)")] | ||||||
|     fn load(&mut self, path: String, py: Python, immediate: bool) -> PyResult<PyObject> { |     fn load(&mut self, path: String, py: Python<'_>, immediate: bool) -> PyResult<PyObject> { | ||||||
|         self.stars_path = Some(path); |         self.stars_path = Some(path); | ||||||
|         if immediate { |         if immediate { | ||||||
|             let stars_path = self.check_stars()?; |             self.router | ||||||
|             let route_res = self.router.load(&stars_path); |                 .load(&self.check_stars()?) | ||||||
|             if let Err(err_msg) = route_res { |                 .map_err(PyErr::new::<PyValueError, _>)?; | ||||||
|                 return Err(PyErr::new::<PyValueError, _>(err_msg)); |  | ||||||
|             }; |  | ||||||
|         } |         } | ||||||
|         Ok(py.None()) |         Ok(py.None()) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     #[pyo3(text_signature = "(/)")] |     #[pyo3(text_signature = "(/)")] | ||||||
|     fn unload(&mut self, py: Python) -> PyObject { |     fn unload(&mut self, py: Python<'_>) -> PyObject { | ||||||
|         self.router.unload(); |         self.router.unload(); | ||||||
|         py.None() |         py.None() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn plot(&mut self, py: Python) -> PyResult<PyObject> { |     fn plot(&mut self, py: Python<'_>) -> PyResult<PyObject> { | ||||||
|         let stars_path = self.check_stars()?; |         let stars_path = self.check_stars()?; | ||||||
|         let route_res = self.router.load(&stars_path); |         let route_res = self.router.load(&stars_path); | ||||||
|         if let Err(err_msg) = route_res { |         if let Err(err_msg) = route_res { | ||||||
|  | @ -155,7 +146,7 @@ impl PyRouter { | ||||||
|         Ok(plot_bbox.to_object(py)) |         Ok(plot_bbox.to_object(py)) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn run_bfs(&mut self, range: f32, py: Python) -> PyResult<PyObject> { |     fn run_bfs(&mut self, range: f32, py: Python<'_>) -> PyResult<PyObject> { | ||||||
|         let stars_path = self.check_stars()?; |         let stars_path = self.check_stars()?; | ||||||
|         let route_res = self.router.load(&stars_path); |         let route_res = self.router.load(&stars_path); | ||||||
|         if let Err(err_msg) = route_res { |         if let Err(err_msg) = route_res { | ||||||
|  | @ -167,7 +158,7 @@ impl PyRouter { | ||||||
|             .map(|_| py.None()) |             .map(|_| py.None()) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn precompute_graph(&mut self, range: f32, py: Python) -> PyResult<PyObject> { |     fn precompute_graph(&mut self, range: f32, py: Python<'_>) -> PyResult<PyObject> { | ||||||
|         let stars_path = self.check_stars()?; |         let stars_path = self.check_stars()?; | ||||||
|         let route_res = self.router.load(&stars_path); |         let route_res = self.router.load(&stars_path); | ||||||
|         if let Err(err_msg) = route_res { |         if let Err(err_msg) = route_res { | ||||||
|  | @ -179,31 +170,29 @@ impl PyRouter { | ||||||
|             .map(|_| py.None()) |             .map(|_| py.None()) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn nb_perf_test(&mut self, range: f32, py: Python) -> PyResult<PyObject> { |     fn nb_perf_test(&mut self, range: f32, py: Python<'_>) -> PyResult<PyObject> { | ||||||
|         let stars_path = self.check_stars()?; |         let stars_path = self.check_stars()?; | ||||||
|         let route_res = self.router.load(&stars_path); |         let route_res = self.router.load(&stars_path); | ||||||
|         if let Err(err_msg) = route_res { |         if let Err(err_msg) = route_res { | ||||||
|             return Err(PyErr::new::<PyValueError, _>(err_msg)); |             return Err(PyErr::new::<PyValueError, _>(err_msg)); | ||||||
|         }; |         }; | ||||||
|         let mut nbmap = BTreeMap::new(); |  | ||||||
|         let tree = self.router.get_tree(); |         let tree = self.router.get_tree(); | ||||||
|         let total_nodes = tree.size(); |         let total_nodes = tree.size(); | ||||||
|  |         let mut total_nbs = 0; | ||||||
|         for (n, node) in tree.iter().enumerate() { |         for (n, node) in tree.iter().enumerate() { | ||||||
|             let nbs = self |             total_nbs += self.router.neighbours(node, range).count(); | ||||||
|                 .router |             // nbmap.insert(node.id, nbs);
 | ||||||
|                 .neighbours(node, range) |  | ||||||
|                 .map(|nb| nb.id) |  | ||||||
|                 .collect::<Vec<_>>(); |  | ||||||
|             nbmap.insert(node.id, nbs); |  | ||||||
|             if n % 100_000 == 0 { |             if n % 100_000 == 0 { | ||||||
|                 println!("{}/{}", n, total_nodes); |                 let avg = total_nbs as f64 / (n + 1) as f64; | ||||||
|  |                 info!("{}/{} {} ({})", n, total_nodes, total_nbs, avg); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         println!("{}", nbmap.len()); |         let avg = total_nbs as f64 / total_nodes as f64; | ||||||
|  |         info!("Total: {} ({})", total_nbs, avg); | ||||||
|         Ok(py.None()) |         Ok(py.None()) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn precompute_neighbors(&mut self, range: f32, py: Python) -> PyResult<PyObject> { |     fn precompute_neighbors(&mut self, range: f32, py: Python<'_>) -> PyResult<PyObject> { | ||||||
|         let stars_path = self.check_stars()?; |         let stars_path = self.check_stars()?; | ||||||
|         let route_res = self.router.load(&stars_path); |         let route_res = self.router.load(&stars_path); | ||||||
|         if let Err(err_msg) = route_res { |         if let Err(err_msg) = route_res { | ||||||
|  | @ -215,6 +204,36 @@ impl PyRouter { | ||||||
|             .map(|_| py.None()) |             .map(|_| py.None()) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     fn bfs_test(&mut self, range: f32) -> PyResult<()> { | ||||||
|  |         use rand::prelude::*; | ||||||
|  |         let stars_path = self.check_stars()?; | ||||||
|  |         let route_res = self.router.load(&stars_path); | ||||||
|  |         if let Err(err_msg) = route_res { | ||||||
|  |             return Err(PyErr::new::<PyValueError, _>(err_msg)); | ||||||
|  |         }; | ||||||
|  |         let mut rng = rand::rngs::StdRng::seed_from_u64(0); | ||||||
|  |         let nodes = self.router.get_tree().size(); | ||||||
|  |         loop { | ||||||
|  |             let source = *self | ||||||
|  |                 .router | ||||||
|  |                 .get_tree() | ||||||
|  |                 .iter() | ||||||
|  |                 .nth(rng.gen_range(0..nodes)) | ||||||
|  |                 .unwrap(); | ||||||
|  |             let goal = *self | ||||||
|  |                 .router | ||||||
|  |                 .get_tree() | ||||||
|  |                 .iter() | ||||||
|  |                 .nth(rng.gen_range(0..nodes)) | ||||||
|  |                 .unwrap(); | ||||||
|  |             self.router.bfs_loop_test(range, &source, &goal, 0); | ||||||
|  |             for w in 0..=15 { | ||||||
|  |                 self.router.bfs_loop_test(range, &source, &goal, 2usize.pow(w)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     #[args(
 |     #[args(
 | ||||||
|         greedyness = "0.5", |         greedyness = "0.5", | ||||||
|         max_dist = "0.0", |         max_dist = "0.0", | ||||||
|  | @ -238,7 +257,7 @@ impl PyRouter { | ||||||
|         let ids: Vec<u32> = match resolve(&hops, &self.router.path, true) { |         let ids: Vec<u32> = match resolve(&hops, &self.router.path, true) { | ||||||
|             Ok(sytems) => sytems.into_iter().map(|id| id.into_id()).collect(), |             Ok(sytems) => sytems.into_iter().map(|id| id.into_id()).collect(), | ||||||
|             Err(err_msg) => { |             Err(err_msg) => { | ||||||
|                 return Err(EdLrrError::ResolveError(err_msg).into()); |                 return Err(err_msg.into()); | ||||||
|             } |             } | ||||||
|         }; |         }; | ||||||
|         let mut is_default = false; |         let mut is_default = false; | ||||||
|  | @ -287,7 +306,7 @@ impl PyRouter { | ||||||
|         return res; |         return res; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn perf_test(&self, callback: PyObject, py: Python) -> PyResult<PyObject> { |     fn perf_test(&self, callback: PyObject, py: Python<'_>) -> PyResult<PyObject> { | ||||||
|         use common::TreeNode; |         use common::TreeNode; | ||||||
|         let node = TreeNode { |         let node = TreeNode { | ||||||
|             pos: [-65.21875, 7.75, -111.03125], |             pos: [-65.21875, 7.75, -111.03125], | ||||||
|  | @ -320,22 +339,34 @@ impl PyRouter { | ||||||
| 
 | 
 | ||||||
|     #[args(grid_size = "1.0")] |     #[args(grid_size = "1.0")] | ||||||
|     #[pyo3(text_signature = "(grid_size)")] |     #[pyo3(text_signature = "(grid_size)")] | ||||||
|     fn get_grid(&self, grid_size: f32, py: Python) -> PyResult<PyObject> { |     fn get_grid(&self, grid_size: f32, py: Python<'_>) -> PyResult<PyObject> { | ||||||
|         let stars_path = self.check_stars()?; |         let stars_path = self.check_stars()?; | ||||||
|         grid_stats(&stars_path, grid_size) |         grid_stats(&stars_path, grid_size) | ||||||
|             .map(|ret| ret.to_object(py)) |             .map(|ret| ret.to_object(py)) | ||||||
|             .map_err(PyErr::new::<PyRuntimeError, _>) |             .map_err(PyErr::new::<PyRuntimeError, _>) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     fn floyd_warshall_test(&mut self, range: f32) -> PyResult<Vec<common::System>> { | ||||||
|  |         let stars_path = self.check_stars()?; | ||||||
|  |         self.router | ||||||
|  |             .load(&stars_path) | ||||||
|  |             .map_err(PyErr::new::<PyValueError, _>)?; | ||||||
|  |         let res = self | ||||||
|  |             .router | ||||||
|  |             .floyd_warshall(range) | ||||||
|  |             .map_err(PyErr::new::<RoutingError, _>)?; | ||||||
|  |         Ok(res) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     #[args(hops = "*")] |     #[args(hops = "*")] | ||||||
|     #[pyo3(text_signature = "(sys_1, sys_2, ..., /)")] |     #[pyo3(text_signature = "(sys_1, sys_2, ..., /)")] | ||||||
|     fn resolve(&self, hops: Vec<SysEntry>, py: Python) -> PyResult<PyObject> { |     fn resolve(&self, hops: Vec<SysEntry>, py: Python<'_>) -> PyResult<PyObject> { | ||||||
|         info!("Resolving systems..."); |         info!("Resolving systems..."); | ||||||
|         let stars_path = self.check_stars()?; |         let stars_path = self.check_stars()?; | ||||||
|         let systems: Vec<System> = match resolve(&hops, &stars_path, false) { |         let systems: Vec<System> = match resolve(&hops, &stars_path, false) { | ||||||
|             Ok(sytems) => sytems.into_iter().map(|sys| sys.into_system()).collect(), |             Ok(sytems) => sytems.into_iter().map(|sys| sys.into_system()).collect(), | ||||||
|             Err(err_msg) => { |             Err(err_msg) => { | ||||||
|                 return Err(EdLrrError::ResolveError(err_msg).into()); |                 return Err(err_msg.into()); | ||||||
|             } |             } | ||||||
|         }; |         }; | ||||||
|         let ret: Vec<(_, System)> = hops |         let ret: Vec<(_, System)> = hops | ||||||
|  | @ -356,15 +387,15 @@ impl PyRouter { | ||||||
|             .map_err(EdLrrError::from)?; |             .map_err(EdLrrError::from)?; | ||||||
|         let mut data: Vec<String> = Vec::with_capacity(CHUNK_SIZE); |         let mut data: Vec<String> = Vec::with_capacity(CHUNK_SIZE); | ||||||
|         let t_start = Instant::now(); |         let t_start = Instant::now(); | ||||||
|         let mut base_id=0; |         let mut base_id = 0; | ||||||
|         let mut wr = BufWriter::new(File::create("test.bktree")?); |         let mut wr = BufWriter::new(File::create("test.bktree")?); | ||||||
|         for sys in reader.into_deserialize::<System>() { |         for sys in reader.into_deserialize::<System>() { | ||||||
|             let sys = sys?; |             let sys = sys?; | ||||||
|             data.push(sys.name); |             data.push(sys.name); | ||||||
|             if data.len()>CHUNK_SIZE { |             if data.len() > CHUNK_SIZE { | ||||||
|                 let tree = BKTree::new(&data, base_id); |                 let tree = BKTree::new(&data, base_id); | ||||||
|                 tree.dump(&mut wr)?; |                 tree.dump(&mut wr)?; | ||||||
|                 base_id=sys.id; |                 base_id = sys.id; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         if !data.is_empty() { |         if !data.is_empty() { | ||||||
|  | @ -375,10 +406,7 @@ impl PyRouter { | ||||||
|         println!("Took: {:?}", t_start.elapsed()); |         println!("Took: {:?}", t_start.elapsed()); | ||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| #[pyproto] |  | ||||||
| impl PyObjectProtocol for PyRouter { |  | ||||||
|     fn __str__(&self) -> PyResult<String> { |     fn __str__(&self) -> PyResult<String> { | ||||||
|         Ok(format!("{:?}", &self)) |         Ok(format!("{:?}", &self)) | ||||||
|     } |     } | ||||||
|  | @ -387,7 +415,6 @@ impl PyObjectProtocol for PyRouter { | ||||||
|         Ok(format!("{:?}", &self)) |         Ok(format!("{:?}", &self)) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 |  | ||||||
| enum ResolveResult { | enum ResolveResult { | ||||||
|     System(System), |     System(System), | ||||||
|     ID(u32), |     ID(u32), | ||||||
|  | @ -409,7 +436,11 @@ impl ResolveResult { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn resolve(entries: &[SysEntry], path: &Path, id_only: bool) -> Result<Vec<ResolveResult>, String> { | fn resolve( | ||||||
|  |     entries: &[SysEntry], | ||||||
|  |     path: &Path, | ||||||
|  |     id_only: bool, | ||||||
|  | ) -> Result<Vec<ResolveResult>, EdLrrError> { | ||||||
|     let mut names: Vec<String> = Vec::new(); |     let mut names: Vec<String> = Vec::new(); | ||||||
|     let mut ret: Vec<u32> = Vec::new(); |     let mut ret: Vec<u32> = Vec::new(); | ||||||
|     let mut needs_rtree = false; |     let mut needs_rtree = false; | ||||||
|  | @ -423,7 +454,10 @@ fn resolve(entries: &[SysEntry], path: &Path, id_only: bool) -> Result<Vec<Resol | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     if !path.exists() { |     if !path.exists() { | ||||||
|         return Err(format!("Source file {:?} does not exist!", path.display())); |         return Err(EdLrrError::ResolveError(format!( | ||||||
|  |             "Source file {:?} does not exist!", | ||||||
|  |             path.display() | ||||||
|  |         ))); | ||||||
|     } |     } | ||||||
|     let name_ids = if !names.is_empty() { |     let name_ids = if !names.is_empty() { | ||||||
|         mmap_csv::mmap_csv(path, names)? |         mmap_csv::mmap_csv(path, names)? | ||||||
|  | @ -439,12 +473,12 @@ fn resolve(entries: &[SysEntry], path: &Path, id_only: bool) -> Result<Vec<Resol | ||||||
|     for ent in entries { |     for ent in entries { | ||||||
|         match ent { |         match ent { | ||||||
|             SysEntry::Name(name) => { |             SysEntry::Name(name) => { | ||||||
|                 let ent_res = name_ids |                 let ent_res = name_ids.get(name).ok_or_else(|| { | ||||||
|                     .get(name) |                     EdLrrError::ResolveError(format!("System {} not found", name)) | ||||||
|                     .ok_or(format!("System {} not found", name))?; |                 })?; | ||||||
|                 let sys = ent_res |                 let sys = ent_res.as_ref().ok_or_else(|| { | ||||||
|                     .as_ref() |                     EdLrrError::ResolveError(format!("System {} not found", name)) | ||||||
|                     .ok_or(format!("System {} not found", name))?; |                 })?; | ||||||
|                 ret.push(*sys); |                 ret.push(*sys); | ||||||
|             } |             } | ||||||
|             SysEntry::ID(id) => ret.push(*id), |             SysEntry::ID(id) => ret.push(*id), | ||||||
|  | @ -453,7 +487,7 @@ fn resolve(entries: &[SysEntry], path: &Path, id_only: bool) -> Result<Vec<Resol | ||||||
|                     .as_ref() |                     .as_ref() | ||||||
|                     .unwrap() |                     .unwrap() | ||||||
|                     .closest(&[*x, *y, *z]) |                     .closest(&[*x, *y, *z]) | ||||||
|                     .ok_or("No systems loaded!")? |                     .ok_or_else(|| EdLrrError::ResolveError("No systems loaded!".to_string()))? | ||||||
|                     .id, |                     .id, | ||||||
|             ), |             ), | ||||||
|         } |         } | ||||||
|  | @ -476,29 +510,17 @@ fn resolve(entries: &[SysEntry], path: &Path, id_only: bool) -> Result<Vec<Resol | ||||||
| struct PyShip { | struct PyShip { | ||||||
|     ship: Ship, |     ship: Ship, | ||||||
| } | } | ||||||
| 
 |  | ||||||
| #[pyproto] |  | ||||||
| impl PyObjectProtocol for PyShip { |  | ||||||
|     fn __str__(&self) -> PyResult<String> { |  | ||||||
|         Ok(format!("{:?}", &self.ship)) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn __repr__(&self) -> PyResult<String> { |  | ||||||
|         Ok(format!("{:?}", &self.ship)) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[pymethods] | #[pymethods] | ||||||
| impl PyShip { | impl PyShip { | ||||||
|     #[staticmethod] |     #[staticmethod] | ||||||
|     fn from_loadout(py: Python, loadout: &str) -> PyResult<PyObject> { |     fn from_loadout(py: Python<'_>, loadout: &str) -> PyResult<PyObject> { | ||||||
|         match Ship::new_from_json(loadout) { |         match Ship::new_from_json(loadout) { | ||||||
|             Ok(ship) => Ok((PyShip { ship: ship.1 }).into_py(py)), |             Ok(ship) => Ok((PyShip { ship: ship.1 }).into_py(py)), | ||||||
|             Err(err_msg) => Err(PyErr::new::<PyValueError, _>(err_msg)), |             Err(err_msg) => Err(PyErr::new::<PyValueError, _>(err_msg)), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     #[staticmethod] |     #[staticmethod] | ||||||
|     fn from_journal(py: Python) -> PyResult<PyObject> { |     fn from_journal(py: Python<'_>) -> PyResult<PyObject> { | ||||||
|         let mut ship = match Ship::new_from_journal() { |         let mut ship = match Ship::new_from_journal() { | ||||||
|             Ok(ship) => ship, |             Ok(ship) => ship, | ||||||
|             Err(err_msg) => { |             Err(err_msg) => { | ||||||
|  | @ -516,38 +538,38 @@ impl PyShip { | ||||||
|         Ok(PyDict::from_sequence(py, ships.to_object(py))?.to_object(py)) |         Ok(PyDict::from_sequence(py, ships.to_object(py))?.to_object(py)) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn to_dict(&self, py: Python) -> PyResult<PyObject> { |     fn to_dict(&self, py: Python<'_>) -> PyResult<PyObject> { | ||||||
|         self.ship.to_object(py) |         self.ship.to_object(py) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     #[pyo3(text_signature = "(dist, /)")] |     #[pyo3(text_signature = "(dist, /)")] | ||||||
|     fn fuel_cost(&self, _py: Python, dist: f32) -> f32 { |     fn fuel_cost(&self, _py: Python<'_>, dist: f32) -> f32 { | ||||||
|         self.ship.fuel_cost(dist) |         self.ship.fuel_cost(dist) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     #[getter] |     #[getter] | ||||||
|     fn range(&self, _py: Python) -> f32 { |     fn range(&self, _py: Python<'_>) -> f32 { | ||||||
|         self.ship.range() |         self.ship.range() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     #[getter] |     #[getter] | ||||||
|     fn max_range(&self, _py: Python) -> f32 { |     fn max_range(&self, _py: Python<'_>) -> f32 { | ||||||
|         self.ship.max_range() |         self.ship.max_range() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     #[pyo3(text_signature = "(dist, /)")] |     #[pyo3(text_signature = "(dist, /)")] | ||||||
|     fn make_jump(&mut self, dist: f32, _py: Python) -> Option<f32> { |     fn make_jump(&mut self, dist: f32, _py: Python<'_>) -> Option<f32> { | ||||||
|         self.ship.make_jump(dist) |         self.ship.make_jump(dist) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     #[pyo3(text_signature = "(dist, /)")] |     #[pyo3(text_signature = "(dist, /)")] | ||||||
|     fn can_jump(&self, dist: f32, _py: Python) -> bool { |     fn can_jump(&self, dist: f32, _py: Python<'_>) -> bool { | ||||||
|         self.ship.can_jump(dist) |         self.ship.can_jump(dist) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     #[args(fuel_amount = "None")] |     #[args(fuel_amount = "None")] | ||||||
|     #[pyo3(text_signature = "(fuel_amount, /)")] |     #[pyo3(text_signature = "(fuel_amount, /)")] | ||||||
|     fn refuel(&mut self, fuel_amount: Option<f32>, _py: Python) { |     fn refuel(&mut self, fuel_amount: Option<f32>, _py: Python<'_>) { | ||||||
|         if let Some(fuel) = fuel_amount { |         if let Some(fuel) = fuel_amount { | ||||||
|             self.ship.fuel_mass = (self.ship.fuel_mass + fuel).min(self.ship.fuel_capacity) |             self.ship.fuel_mass = (self.ship.fuel_mass + fuel).min(self.ship.fuel_capacity) | ||||||
|         } else { |         } else { | ||||||
|  | @ -556,9 +578,17 @@ impl PyShip { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     #[pyo3(text_signature = "(factor, /)")] |     #[pyo3(text_signature = "(factor, /)")] | ||||||
|     fn boost(&mut self, factor: f32, _py: Python) { |     fn boost(&mut self, factor: f32, _py: Python<'_>) { | ||||||
|         self.ship.boost(factor); |         self.ship.boost(factor); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     fn __str__(&self) -> PyResult<String> { | ||||||
|  |         Ok(format!("{:?}", &self.ship)) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn __repr__(&self) -> PyResult<String> { | ||||||
|  |         Ok(format!("{:?}", &self.ship)) | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl PyShip { | impl PyShip { | ||||||
|  | @ -572,14 +602,14 @@ fn preprocess_edsm( | ||||||
|     _bodies_path: &str, |     _bodies_path: &str, | ||||||
|     _systems_path: &str, |     _systems_path: &str, | ||||||
|     _out_path: &str, |     _out_path: &str, | ||||||
|     _py: Python, |     _py: Python<'_>, | ||||||
| ) -> PyResult<()> { | ) -> PyResult<()> { | ||||||
|     Err(pyo3::exceptions::PyNotImplementedError::new_err( |     Err(pyo3::exceptions::PyNotImplementedError::new_err( | ||||||
|         "please use Spansh's Galaxy dump and preprocess_galaxy()", |         "please use Spansh's Galaxy dump and preprocess_galaxy()", | ||||||
|     )) |     )) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn to_py_value(value: eval::Value, py: Python) -> PyResult<PyObject> { | fn to_py_value(value: eval::Value, py: Python<'_>) -> PyResult<PyObject> { | ||||||
|     type Value = eval::Value; |     type Value = eval::Value; | ||||||
|     match value { |     match value { | ||||||
|         Value::String(s) => Ok(s.to_object(py)), |         Value::String(s) => Ok(s.to_object(py)), | ||||||
|  | @ -611,14 +641,14 @@ fn to_py_value(value: eval::Value, py: Python) -> PyResult<PyObject> { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn to_py(res: Result<eval::Value, eval::Error>, py: Python) -> PyResult<PyObject> { | fn to_py(res: Result<eval::Value, eval::Error>, py: Python<'_>) -> PyResult<PyObject> { | ||||||
|     res.map_err(|e| PyErr::from(EdLrrError::EvalError(e))) |     res.map_err(|e| PyErr::from(EdLrrError::EvalError(e))) | ||||||
|         .and_then(|r| to_py_value(r, py)) |         .and_then(|r| to_py_value(r, py)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[pyfunction] | #[pyfunction] | ||||||
| #[pyo3(text_signature = "(expr)")] | #[pyo3(text_signature = "(expr)")] | ||||||
| fn expr_test(expr: &str, py: Python) -> PyResult<PyObject> { | fn expr_test(expr: &str, py: Python<'_>) -> PyResult<PyObject> { | ||||||
|     use eval::{to_value, Expr, Value}; |     use eval::{to_value, Expr, Value}; | ||||||
|     let mut res = Expr::new(expr) |     let mut res = Expr::new(expr) | ||||||
|         .compile() |         .compile() | ||||||
|  | @ -647,7 +677,7 @@ fn preprocess_galaxy(path: &str, out_path: &str) -> PyResult<()> { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[pymodule] | #[pymodule] | ||||||
| pub fn _ed_lrr(_py: Python, m: &PyModule) -> PyResult<()> { | pub fn _ed_lrr(_py: Python<'_>, m: &PyModule) -> PyResult<()> { | ||||||
|     better_panic::install(); |     better_panic::install(); | ||||||
|     pyo3_log::init(); |     pyo3_log::init(); | ||||||
|     profiling::init(); |     profiling::init(); | ||||||
|  |  | ||||||
|  | @ -1,26 +1,62 @@ | ||||||
| use crate::common::{EdLrrError, EdLrrResult, System}; | use crate::common::{EdLrrError, EdLrrResult, System}; | ||||||
| use crate::info; | use crate::info; | ||||||
|  | use crossbeam_channel::bounded; | ||||||
| use csv_core::{ReadFieldResult, Reader}; | use csv_core::{ReadFieldResult, Reader}; | ||||||
|  | use dashmap::DashMap; | ||||||
|  | use eyre::Result; | ||||||
|  | use itertools::Itertools; | ||||||
| use memmap::Mmap; | use memmap::Mmap; | ||||||
| use std::collections::HashMap; | use std::collections::HashMap; | ||||||
| use std::fs::File; | use std::fs::File; | ||||||
| use std::path::Path; | use std::path::Path; | ||||||
|  | use std::sync::Arc; | ||||||
| 
 | 
 | ||||||
| pub fn mmap_csv(path: &Path, query: Vec<String>) -> Result<HashMap<String, Option<u32>>, String> { | struct MmapCsv { | ||||||
|     let file = File::open(path).map_err(|e| e.to_string())?; |     mm: Mmap, | ||||||
|     let mm = unsafe { Mmap::map(&file) }.map_err(|e| e.to_string())?; | } | ||||||
|     let mut best = query | 
 | ||||||
|         .iter() | impl MmapCsv { | ||||||
|         .map(|s| (s, (s.as_bytes(), usize::MAX, u32::MAX))) |     fn new(path: &Path) -> Result<Self> { | ||||||
|         .collect::<Vec<(&String, (_, usize, u32))>>(); |         let file = File::open(path)?; | ||||||
|     let t_start = std::time::Instant::now(); |         let mm = unsafe { Mmap::map(&file) }?; | ||||||
|     let dist = eddie::slice::DamerauLevenshtein::new(); |         Ok(Self { mm }) | ||||||
|     let mut row = 0; |     } | ||||||
|     { | 
 | ||||||
|         let mut data = &mm[..]; |     fn search(&self, query: Vec<String>) -> Result<HashMap<String, Option<u32>>, EdLrrError> { | ||||||
|  |         let t_start = std::time::Instant::now(); | ||||||
|  |         let map = Arc::new(DashMap::new()); | ||||||
|  |         let (tx, rx) = bounded(1024); | ||||||
|  |         let query_b = query.iter().map(|s| s.bytes().collect_vec()).collect_vec(); | ||||||
|  |         let mut workers = (0..(num_cpus::get())) | ||||||
|  |             .map(|_| { | ||||||
|  |                 let query_b = query_b.clone(); | ||||||
|  |                 let query = query.clone(); | ||||||
|  |                 let rx = rx.clone(); | ||||||
|  |                 let map = map.clone(); | ||||||
|  |                 std::thread::spawn(move || { | ||||||
|  |                     let dist = eddie::slice::DamerauLevenshtein::new(); | ||||||
|  |                     rx.into_iter() | ||||||
|  |                         // .flatten()
 | ||||||
|  |                         .for_each(|(id, name): (_, Vec<u8>)| { | ||||||
|  |                             for (query, query_b) in query.iter().zip(query_b.iter()) { | ||||||
|  |                                 let d = dist.distance(name.as_slice(), query_b); | ||||||
|  |                                 let mut e = map.entry(query.clone()).or_insert((usize::MAX, None)); | ||||||
|  |                                 if d < e.0 { | ||||||
|  |                                     *e = (d, Some(id)); | ||||||
|  |                                 } | ||||||
|  |                             } | ||||||
|  |                         }); | ||||||
|  |                 }) | ||||||
|  |             }) | ||||||
|  |             .collect_vec(); | ||||||
|  |         drop(rx); | ||||||
|  |         let mut data = &self.mm[..]; | ||||||
|         let mut rdr = Reader::new(); |         let mut rdr = Reader::new(); | ||||||
|         let mut field = [0; 1024]; |         let mut field = [0; 1024]; | ||||||
|         let mut fieldidx = 0; |         let mut fieldidx = 0; | ||||||
|  |         // let mut chunk = vec![];
 | ||||||
|  |         let mut sys_id = 0u32; | ||||||
|  |         let mut row = 0; | ||||||
|         loop { |         loop { | ||||||
|             let (result, nread, nwrite) = rdr.read_field(data, &mut field); |             let (result, nread, nwrite) = rdr.read_field(data, &mut field); | ||||||
|             data = &data[nread..]; |             data = &data[nread..]; | ||||||
|  | @ -28,18 +64,22 @@ pub fn mmap_csv(path: &Path, query: Vec<String>) -> Result<HashMap<String, Optio | ||||||
|             match result { |             match result { | ||||||
|                 ReadFieldResult::InputEmpty => {} |                 ReadFieldResult::InputEmpty => {} | ||||||
|                 ReadFieldResult::OutputFull => { |                 ReadFieldResult::OutputFull => { | ||||||
|                     return Err("Encountered field larget than 1024 bytes!".to_string()); |                     return Err(EdLrrError::ResolveError( | ||||||
|  |                         "Encountered field larget than 1024 bytes!".to_string(), | ||||||
|  |                     )); | ||||||
|                 } |                 } | ||||||
|                 ReadFieldResult::Field { record_end } => { |                 ReadFieldResult::Field { record_end } => { | ||||||
|                     if fieldidx == 1 { |                     match fieldidx { | ||||||
|                         for (_, (name_b, best_dist, id)) in best.iter_mut() { |                         0 => { | ||||||
|                             let d = dist.distance(name_b, field); |                             sys_id = unsafe { std::str::from_utf8_unchecked(field) } | ||||||
|                             if d < *best_dist { |                                 .parse::<u32>() | ||||||
|                                 *best_dist = d; |                                 .unwrap(); | ||||||
|                                 *id = row; |  | ||||||
|                             } |  | ||||||
|                         } |                         } | ||||||
|                     } |                         1 => tx | ||||||
|  |                             .send((sys_id, field.to_vec())) | ||||||
|  |                             .map_err(|e| EdLrrError::ResolveError(e.to_string()))?, | ||||||
|  |                         _ => (), | ||||||
|  |                     }; | ||||||
|                     if record_end { |                     if record_end { | ||||||
|                         fieldidx = 0; |                         fieldidx = 0; | ||||||
|                         row += 1; |                         row += 1; | ||||||
|  | @ -54,16 +94,28 @@ pub fn mmap_csv(path: &Path, query: Vec<String>) -> Result<HashMap<String, Optio | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |         drop(tx); | ||||||
|  |         for w in workers.drain(..) { | ||||||
|  |             w.join().unwrap(); | ||||||
|  |         } | ||||||
|  |         let res = Arc::try_unwrap(map) | ||||||
|  |             .unwrap() | ||||||
|  |             .into_iter() | ||||||
|  |             .map(|(k, (_, id))| (k, id)) | ||||||
|  |             .collect::<HashMap<_, _>>(); | ||||||
|  |         let rate = (row as f64) / t_start.elapsed().as_secs_f64(); | ||||||
|  |         info!( | ||||||
|  |             "Took: {:.2?}, {:.2} systems/second", | ||||||
|  |             t_start.elapsed(), | ||||||
|  |             rate | ||||||
|  |         ); | ||||||
|  |         Ok(res) | ||||||
|     } |     } | ||||||
|     let search_result = best | } | ||||||
|         .drain(..) | 
 | ||||||
|         .map(|(query_name, (_, _, idx))| (query_name.clone(), Some(idx))) | pub fn mmap_csv( | ||||||
|         .collect::<HashMap<String, Option<u32>>>(); |     path: &Path, | ||||||
|     let rate = (row as f64) / t_start.elapsed().as_secs_f64(); |     query: Vec<String>, | ||||||
|     info!( | ) -> Result<HashMap<String, Option<u32>>, EdLrrError> { | ||||||
|         "Took: {:.2?}, {:.2} systems/second", |     MmapCsv::new(path)?.search(query) | ||||||
|         t_start.elapsed(), |  | ||||||
|         rate |  | ||||||
|     ); |  | ||||||
|     Ok(search_result) |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -7,6 +7,7 @@ use crate::profiling::{span, Level}; | ||||||
| use crate::ship::Ship; | use crate::ship::Ship; | ||||||
| 
 | 
 | ||||||
| use crossbeam_channel::{bounded, unbounded, Receiver, SendError, Sender}; | use crossbeam_channel::{bounded, unbounded, Receiver, SendError, Sender}; | ||||||
|  | use dashmap::{DashMap, DashSet}; | ||||||
| use derivative::Derivative; | use derivative::Derivative; | ||||||
| use dict_derive::IntoPyObject; | use dict_derive::IntoPyObject; | ||||||
| 
 | 
 | ||||||
|  | @ -18,6 +19,8 @@ use permutohedron::LexicalPermutation; | ||||||
| 
 | 
 | ||||||
| use pyo3::prelude::*; | use pyo3::prelude::*; | ||||||
| use pythonize::depythonize; | use pythonize::depythonize; | ||||||
|  | use rayon::prelude::*; | ||||||
|  | use rayon::ThreadPoolBuilder; | ||||||
| use rstar::{PointDistance, RStarInsertionStrategy, RTree, RTreeObject, RTreeParams, AABB}; | use rstar::{PointDistance, RStarInsertionStrategy, RTree, RTreeObject, RTreeParams, AABB}; | ||||||
| use rustc_hash::{FxHashMap, FxHashSet}; | use rustc_hash::{FxHashMap, FxHashSet}; | ||||||
| use serde::{Deserialize, Serialize}; | use serde::{Deserialize, Serialize}; | ||||||
|  | @ -27,10 +30,11 @@ use std::fs::File; | ||||||
| use std::hash::{Hash, Hasher}; | use std::hash::{Hash, Hasher}; | ||||||
| use std::io::{BufReader, BufWriter, Write}; | use std::io::{BufReader, BufWriter, Write}; | ||||||
| use std::path::PathBuf; | use std::path::PathBuf; | ||||||
|  | use std::sync::atomic::{AtomicUsize, Ordering}; | ||||||
| use std::sync::{Arc, Mutex}; | use std::sync::{Arc, Mutex}; | ||||||
| use std::thread; | use std::thread; | ||||||
| use std::thread::JoinHandle; | use std::thread::JoinHandle; | ||||||
| use std::time::Instant; | use std::time::{Duration, Instant}; | ||||||
| use std::{ | use std::{ | ||||||
|     collections::{BinaryHeap, VecDeque}, |     collections::{BinaryHeap, VecDeque}, | ||||||
|     path::Path, |     path::Path, | ||||||
|  | @ -317,8 +321,8 @@ impl TryFrom<PyModeConfig> for ModeConfig { | ||||||
| #[allow(non_camel_case_types)] | #[allow(non_camel_case_types)] | ||||||
| pub enum PrecomputeMode { | pub enum PrecomputeMode { | ||||||
|     Full, |     Full, | ||||||
|     Route_From, |     Route_From(u32), | ||||||
|     Route_To, |     Route_To(u32), | ||||||
|     None, |     None, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -817,6 +821,74 @@ impl Router { | ||||||
|         return self.scoopable.contains(&id); |         return self.scoopable.contains(&id); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     pub fn bfs_loop_test(&self, range: f32, source: &TreeNode, goal: &TreeNode, n: usize) -> (bool, usize, usize) { | ||||||
|  |         // info!("Starting thread pool");
 | ||||||
|  |         // ThreadPoolBuilder::new()
 | ||||||
|  |         //     .num_threads(8)
 | ||||||
|  |         //     .build_global()
 | ||||||
|  |         //     .unwrap();
 | ||||||
|  |         let t_start = Instant::now(); | ||||||
|  |         let route_dist = dist(&source.pos, &goal.pos); | ||||||
|  |         let seen: Arc<DashMap<u32, u32>> = Arc::new(DashMap::new()); | ||||||
|  |         let mut depth = 0; | ||||||
|  |         let mut queue = vec![*source]; | ||||||
|  |         let mut queue_next = vec![]; | ||||||
|  |         let tree = self.tree.clone(); | ||||||
|  |         let r2 = range * range; | ||||||
|  |         let mut found = false; | ||||||
|  |         while !queue.is_empty() { | ||||||
|  |             depth += 1; | ||||||
|  |             let seen = seen.clone(); | ||||||
|  |             queue_next.extend(queue.drain(..).flat_map(|sys| { | ||||||
|  |                 let seen = seen.clone(); | ||||||
|  |                 tree.locate_within_distance(sys.pos, r2) | ||||||
|  |                     .filter_map(move |nb| seen.insert(nb.id, sys.id).is_none().then_some(*nb)) | ||||||
|  |             })); | ||||||
|  |             if seen.contains_key(&goal.id) { | ||||||
|  |                 found = true; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             std::mem::swap(&mut queue_next, &mut queue); | ||||||
|  |             if n != 0 { | ||||||
|  |                 queue.sort_by_cached_key(|v| F32(heuristic(range, v, goal))); | ||||||
|  |                 queue.truncate(n); | ||||||
|  |             } | ||||||
|  |             // info!("[{}|{}] {}", goal.id, depth, queue.len());
 | ||||||
|  |         } | ||||||
|  |         let seen = Arc::try_unwrap(seen) | ||||||
|  |             .unwrap() | ||||||
|  |             .into_iter() | ||||||
|  |             .collect::<FxHashMap<u32, u32>>(); | ||||||
|  |         info!( | ||||||
|  |             "[{}|{}->{} ({:.02} Ly)|{}] Depth: {} Seen: {} ({:.02}%) Took: {}", | ||||||
|  |             n, | ||||||
|  |             source.id, | ||||||
|  |             goal.id, | ||||||
|  |             route_dist, | ||||||
|  |             found, | ||||||
|  |             depth, | ||||||
|  |             seen.len(), | ||||||
|  |             ((seen.len() as f64) / (tree.size() as f64)) * 100.0, | ||||||
|  |             humantime::format_duration(t_start.elapsed()) | ||||||
|  |         ); | ||||||
|  |         return (found, depth, seen.len()); | ||||||
|  |         let path=self.reconstruct(goal.id, &seen); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn reconstruct(&self, goal_id: u32, map: &FxHashMap<u32, u32>) -> Result<Vec<System>, String> { | ||||||
|  |         let mut path = vec![]; | ||||||
|  |         let mut current = goal_id; | ||||||
|  |         while let Some(next) = map.get(¤t) { | ||||||
|  |             path.push( | ||||||
|  |                 self.get(*next)? | ||||||
|  |                     .ok_or(format!("System ID {} not found", next))?, | ||||||
|  |             ); | ||||||
|  |             current = *next; | ||||||
|  |         } | ||||||
|  |         path.reverse(); | ||||||
|  |         Ok(path) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     fn best_multiroute( |     fn best_multiroute( | ||||||
|         &mut self, |         &mut self, | ||||||
|         waypoints: &[System], |         waypoints: &[System], | ||||||
|  | @ -1468,7 +1540,9 @@ impl Router { | ||||||
|                 let mut refuels = state.refuels; |                 let mut refuels = state.refuels; | ||||||
|                 let dist = dist(&nb.pos, &state.node.pos); |                 let dist = dist(&nb.pos, &state.node.pos); | ||||||
|                 let (fuel_cost, new_fuel) = { |                 let (fuel_cost, new_fuel) = { | ||||||
|                     if let Some(res) = ship.fuel_cost_for_jump(state.fuel, dist, state.node.get_mult()) { |                     if let Some(res) = | ||||||
|  |                         ship.fuel_cost_for_jump(state.fuel, dist, state.node.get_mult()) | ||||||
|  |                     { | ||||||
|                         // can jump with current amount of fuel
 |                         // can jump with current amount of fuel
 | ||||||
|                         res |                         res | ||||||
|                     } else if let Some(res) = |                     } else if let Some(res) = | ||||||
|  | @ -1544,7 +1618,22 @@ impl Router { | ||||||
|             }) |             }) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn floyd_warshall(&self, _range: f32) { |     pub fn floyd_warshall(&self, range: f32) -> Result<Vec<System>, String> { | ||||||
|  |         let mut dist: FxHashMap<u64, usize> = FxHashMap::default(); | ||||||
|  |         info!("nb..."); | ||||||
|  |         let total = self.tree.size(); | ||||||
|  |         for (n, node) in self.tree.iter().enumerate() { | ||||||
|  |             if (n % 100_000) == 0 { | ||||||
|  |                 println!("{}/{}", n, total); | ||||||
|  |             } | ||||||
|  |             let key = (node.id as u64) << 32; | ||||||
|  |             for nb in self.neighbours(node, range) { | ||||||
|  |                 let key = key | nb.id as u64; | ||||||
|  |                 dist.entry(key).or_insert(1); | ||||||
|  |             } | ||||||
|  |             let key = ((node.id as u64) << 32) | node.id as u64; | ||||||
|  |             dist.insert(key, 0); | ||||||
|  |         } | ||||||
|         todo!() |         todo!() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -1553,86 +1642,81 @@ impl Router { | ||||||
|         h = (dist(node,goal)-(range*node.mult)).max(0.0) // remaining distance after jumping from here
 |         h = (dist(node,goal)-(range*node.mult)).max(0.0) // remaining distance after jumping from here
 | ||||||
|         */ |         */ | ||||||
|         let src = self.tree.nearest_neighbor(&[0.0, 0.0, 0.0]).unwrap(); |         let src = self.tree.nearest_neighbor(&[0.0, 0.0, 0.0]).unwrap(); | ||||||
|  |         // let mut route_log = BufWriter::new(File::create("route_log_ib.txt").map_err(|e| e.to_string())?);
 | ||||||
|         let goal = self |         let goal = self | ||||||
|             .tree |             .tree | ||||||
|             // .nearest_neighbor(&[-1111.5625, -134.21875, 65269.75]) // Beagle Point
 |             .nearest_neighbor(&[-1111.5625, -134.21875, 65269.75]) // Beagle Point
 | ||||||
|             .nearest_neighbor(&[-9530.5, -910.28125, 19808.125]) // Colonia
 |             // .nearest_neighbor(&[-9530.5, -910.28125, 19808.125]) // Colonia
 | ||||||
|             .unwrap(); |             .unwrap(); | ||||||
|         let mut best_node = FxHashMap::default(); |         let mut best_node = FxHashMap::default(); | ||||||
|         let mut prev = FxHashMap::default(); |         // let mut prev = FxHashMap::default();
 | ||||||
|         let mut wait_list: FxHashMap<usize, MinFHeap<TreeNode>> = FxHashMap::default(); |         let mut queue = MinFHeap::new(); | ||||||
|         let mut in_wait_list: FxHashSet<u32> = FxHashSet::default(); |         let t_start = Instant::now(); | ||||||
|  |         let mut n = 0usize; | ||||||
|  |         let mut skipped = 0usize; | ||||||
|  |         let mut global_best = u32::MAX; | ||||||
|  |         queue.push(heuristic(range, src, goal), (0, src)); | ||||||
|         loop { |         loop { | ||||||
|             let t_start = Instant::now(); |             println!("Q: {}", queue.len()); | ||||||
|             let mut n = 0usize; |             if queue.is_empty() { | ||||||
|             let mut skipped = 0usize; |                 warn!( | ||||||
|             let mut depth = 0usize; |                     "Visited: {} | Skipped: {} | search space exhausted after {}", | ||||||
|             let mut queue = VecDeque::new(); |                     n, | ||||||
|             queue.push_back(*src); |                     skipped, | ||||||
|             'outer: loop { |                     humantime::format_duration(t_start.elapsed()) | ||||||
|                 // println!("D: {} | Q: {}", depth, queue.len());
 |                 ); | ||||||
|                 let mut queue_next = VecDeque::new(); |                 break; | ||||||
|                 if queue.is_empty() { |             } | ||||||
|                     warn!( |             while let Some((_, (depth, node))) = queue.pop() { | ||||||
|                         "Depth: {} | Visited: {} | Skipped: {} | search space exhausted after {}", |                 let best_len = best_node.len(); | ||||||
|                         depth, |                 let best_depth = best_node.entry(node.id).or_insert(depth); | ||||||
|                         n, |                 if *best_depth > global_best { | ||||||
|                         skipped, |                     skipped += 1; | ||||||
|                         humantime::format_duration(t_start.elapsed()) |                     continue; | ||||||
|                     ); |  | ||||||
|                     break; |  | ||||||
|                 } |                 } | ||||||
|                 while let Some(node) = queue.pop_front() { |                 // writeln!(route_log,"{}, {}",node.id,depth).map_err(|e| e.to_string())?;
 | ||||||
|                     let best_len = best_node.len(); |                 // route_log.flush().map_err(|e| e.to_string())?;
 | ||||||
|                     let best_depth = best_node.entry(node.id).or_insert(depth); |                 if depth < *best_depth { | ||||||
|                     if depth > *best_depth { |                     *best_depth = depth; | ||||||
|                         skipped += 1; |                 } | ||||||
|                         continue; |                 n += 1; | ||||||
|                     } |                 if node.id == goal.id { | ||||||
|                     if depth < *best_depth { |                     if depth < global_best { | ||||||
|                         *best_depth = depth; |                         global_best = global_best.min(depth); | ||||||
|                     } |                         queue.retain(|(_, (d, _))| *d <= global_best); | ||||||
|                     n += 1; |  | ||||||
|                     if node.id == goal.id { |  | ||||||
|                         info!( |                         info!( | ||||||
|                             "Depth: {}, Skipped: {}, Seen: {} (Total: {}) | Best: {} | elapsed: {}", |                             "Queued: {}, Skipped: {}, Seen: {} (Total: {}) | Best: {} | elapsed: {}", | ||||||
|                             depth, |                             queue.len(), | ||||||
|                             skipped, |                             skipped, | ||||||
|                             n, |                             n, | ||||||
|                             best_len, |                             best_len, | ||||||
|                             best_depth, |                             global_best, | ||||||
|                             humantime::format_duration(t_start.elapsed()).to_string() |                             humantime::format_duration(t_start.elapsed()).to_string() | ||||||
|                         ); |                         ); | ||||||
|                         for layer_n in wait_list.keys().sorted() { |  | ||||||
|                             println!("WL({}): {}", layer_n, wait_list[layer_n].len()); |  | ||||||
|                         } |  | ||||||
|                         todo!(); |  | ||||||
|                         break 'outer; |  | ||||||
|                     } |                     } | ||||||
|                     let valid_nbs = self |                     continue; | ||||||
|                         .neighbours(&node, node.get_mult() * range) |                 } else if n % 10000 == 0 { | ||||||
|                         .filter(|nb| (self.valid(nb.id) || (nb.id == goal.id))) |                     info!( | ||||||
|                         .filter(|nb| match best_node.get(&nb.id) { |                         "Queued: {}, Skipped: {}, Seen: {} (Total: {}) | Best: {} | elapsed: {}", | ||||||
|                             Some(&d) => (depth + 1) <= d, |                         queue.len(), | ||||||
|                             None => true, |                         skipped, | ||||||
|                         }) |                         n, | ||||||
|                         .map(|nb| { |                         best_len, | ||||||
|                             prev.insert(nb.id, node); |                         global_best, | ||||||
|                             (F32(heuristic(range, nb, goal)), *nb) |                         humantime::format_duration(t_start.elapsed()).to_string() | ||||||
|                         }); |                     ); | ||||||
|                     queue_next.extend(valid_nbs); |  | ||||||
|                 } |                 } | ||||||
|                 queue_next.make_contiguous().sort(); |                 self.neighbours(node, node.get_mult() * range) | ||||||
|                 if let Some((_, nb)) = queue_next.pop_front() { |                     .filter(|nb| (self.valid(nb.id) || (nb.id == goal.id))) | ||||||
|                     queue.push_back(nb); |                     .filter(|nb| match best_node.get(&nb.id) { | ||||||
|                 } |                         Some(&d) => depth < d, | ||||||
|                 let layer = wait_list.entry(depth).or_default(); |                         None => true, | ||||||
|                 while let Some((F32(v), nb)) = queue_next.pop_front() { |                     }) | ||||||
|                     if in_wait_list.insert(nb.id) { |                     .map(|nb| (heuristic(range, nb, goal), nb)) | ||||||
|                         layer.push(v, nb); |                     .for_each(|(h, nb)| { | ||||||
|                     }; |                         // prev.insert(nb.id, node.id);
 | ||||||
|                 } |                         queue.push(h, (depth + 1, nb)); | ||||||
|                 depth += 1; |                     }); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         todo!() |         todo!() | ||||||
|  | @ -1705,7 +1789,7 @@ impl Router { | ||||||
|                 let tx = tx_r.clone(); |                 let tx = tx_r.clone(); | ||||||
|                 let rx = rx_q.clone(); |                 let rx = rx_q.clone(); | ||||||
|                 thread::spawn(move || { |                 thread::spawn(move || { | ||||||
|                     while let Ok(nodes) = rx.recv() { |                     rx.into_iter().for_each(|nodes| { | ||||||
|                         let mut ret = vec![]; |                         let mut ret = vec![]; | ||||||
|                         for node in nodes { |                         for node in nodes { | ||||||
|                             let res: Vec<TreeNode> = |                             let res: Vec<TreeNode> = | ||||||
|  | @ -1713,9 +1797,8 @@ impl Router { | ||||||
|                             ret.push((node, res)); |                             ret.push((node, res)); | ||||||
|                         } |                         } | ||||||
|                         tx.send(ret).unwrap(); |                         tx.send(ret).unwrap(); | ||||||
|                     } |                     }); | ||||||
|                     drop(tx); |                     drop(tx); | ||||||
|                     drop(rx); |  | ||||||
|                 }) |                 }) | ||||||
|             }) |             }) | ||||||
|             .collect(); |             .collect(); | ||||||
|  | @ -1784,49 +1867,31 @@ impl Router { | ||||||
| 
 | 
 | ||||||
|     #[cfg_attr(feature = "profiling", tracing::instrument)] |     #[cfg_attr(feature = "profiling", tracing::instrument)] | ||||||
|     pub fn precompute_all(&mut self, range: f32) -> Result<(), String> { |     pub fn precompute_all(&mut self, range: f32) -> Result<(), String> { | ||||||
|  |         use flate2::write::GzEncoder; | ||||||
|         let fh_nb = File::create(format!(r#"O:\nb_{}.dat"#, range)).unwrap(); |         let fh_nb = File::create(format!(r#"O:\nb_{}.dat"#, range)).unwrap(); | ||||||
|         let mut buf_writer = BufWriter::new(fh_nb); |         let mut fh_encoder = BufWriter::new(fh_nb); | ||||||
|         let mut pos: u64 = 0; |         let mut pos: u64 = 0; | ||||||
|  |         let mut n = 0; | ||||||
|         let total = self.tree.size(); |         let total = self.tree.size(); | ||||||
|         let (tx, rx, threads) = self.neighbor_workers(num_cpus::get(), range); |         // let (tx, rx, threads) = self.neighbor_workers(num_cpus::get(), range);
 | ||||||
|         let mut n: usize = 0; |  | ||||||
|         let mut map: FxHashMap<u32, u64> = FxHashMap::default(); |         let mut map: FxHashMap<u32, u64> = FxHashMap::default(); | ||||||
|         info!("Precomputing neighbor map"); |         info!("Precomputing neighbor map..."); | ||||||
|         info!("Sumbitting jobs"); |         self.tree.iter().for_each(|node| { | ||||||
|         self.tree |             let nb = self.neighbours(node, range).map(|nb| nb.id).collect_vec(); | ||||||
|             .iter() |             map.insert(node.id, pos); | ||||||
|             .chunks(10_000) |             pos += fh_encoder.write(&bincode::serialize(&nb).unwrap()).unwrap() as u64; | ||||||
|             .into_iter() |             if (n % 10000) == 0 { | ||||||
|             .for_each(|chunk| { |                 let prc = ((n as f64) / (total as f64)) * 100f64; | ||||||
|                 tx.send(chunk.cloned().collect()).unwrap(); |                 info!("{}/{} ({:.2}%) done, {} bytes", n, total, prc, pos); | ||||||
|             }); |             } | ||||||
|         drop(tx); |             n += 1; | ||||||
|         info!("Processing..."); |         }); | ||||||
|         rx.into_iter() |  | ||||||
|             .flatten() |  | ||||||
|             .enumerate() |  | ||||||
|             .for_each(|(n, (node, mut neighbors))| { |  | ||||||
|                 let neighbors: Vec<u32> = neighbors.drain(..).map(|n| n.id).collect(); |  | ||||||
|                 // map.insert(node.id, pos);
 |  | ||||||
|                 pos += buf_writer |  | ||||||
|                     .write(&bincode::serialize(&neighbors).unwrap()) |  | ||||||
|                     .unwrap() as u64; |  | ||||||
|                 if (n % 100000) == 0 { |  | ||||||
|                     let prc = ((n as f64) / (total as f64)) * 100f64; |  | ||||||
|                     info!("{}/{} ({:.2}%) done, {} bytes", n, total, prc, pos); |  | ||||||
|                 } |  | ||||||
|             }); |  | ||||||
|         let mut fh_idx = BufWriter::new(File::create(format!(r#"O:\nb_{}.idx"#, range)).unwrap()); |         let mut fh_idx = BufWriter::new(File::create(format!(r#"O:\nb_{}.idx"#, range)).unwrap()); | ||||||
|         info!("Writing index map"); |         info!("Writing index map"); | ||||||
|         info!( |         info!( | ||||||
|             "Wrote {} bytes", |             "Wrote {} bytes", | ||||||
|             fh_idx.write(&bincode::serialize(&map).unwrap()).unwrap() |             fh_idx.write(&bincode::serialize(&map).unwrap()).unwrap() | ||||||
|         ); |         ); | ||||||
|         info!("Joining threads"); |  | ||||||
|         for t in threads { |  | ||||||
|             t.join().unwrap(); |  | ||||||
|         } |  | ||||||
|         info!("Done!"); |  | ||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -2476,13 +2541,15 @@ impl Router { | ||||||
|                 let next_depth = depth + 1; |                 let next_depth = depth + 1; | ||||||
|                 match node { |                 match node { | ||||||
|                     BiDirNode::Forward(node) => { |                     BiDirNode::Forward(node) => { | ||||||
|                         let nbs = self.neighbours(&node, node.get_mult() * range).filter_map(|nb| { |                         let nbs = | ||||||
|                             if !seen_fwd.insert(nb.id) { |                             self.neighbours(&node, node.get_mult() * range) | ||||||
|                                 return None; |                                 .filter_map(|nb| { | ||||||
|                             } |                                     if !seen_fwd.insert(nb.id) { | ||||||
|                             prev.insert(nb.id, node.id); |                                         return None; | ||||||
|                             Some((next_depth, BiDirNode::Forward(*nb))) |                                     } | ||||||
|                         }); |                                     prev.insert(nb.id, node.id); | ||||||
|  |                                     Some((next_depth, BiDirNode::Forward(*nb))) | ||||||
|  |                                 }); | ||||||
|                         queue.extend(nbs); |                         queue.extend(nbs); | ||||||
|                     } |                     } | ||||||
|                     BiDirNode::Backwards(node) => { |                     BiDirNode::Backwards(node) => { | ||||||
|  |  | ||||||
|  | @ -229,7 +229,7 @@ impl Ship { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl FSD { | impl FSD { | ||||||
|     pub fn to_object(&self, py: Python) -> PyResult<PyObject> { |     pub fn to_object(&self, py: Python<'_>) -> PyResult<PyObject> { | ||||||
|         let elem = PyDict::new(py); |         let elem = PyDict::new(py); | ||||||
|         elem.set_item("rating_val", self.rating_val)?; |         elem.set_item("rating_val", self.rating_val)?; | ||||||
|         elem.set_item("class_val", self.class_val)?; |         elem.set_item("class_val", self.class_val)?; | ||||||
|  | @ -242,7 +242,7 @@ impl FSD { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Ship { | impl Ship { | ||||||
|     pub fn to_object(&self, py: Python) -> PyResult<PyObject> { |     pub fn to_object(&self, py: Python<'_>) -> PyResult<PyObject> { | ||||||
|         let elem = PyDict::new(py); |         let elem = PyDict::new(py); | ||||||
|         elem.set_item("base_mass", self.base_mass)?; |         elem.set_item("base_mass", self.base_mass)?; | ||||||
|         elem.set_item("fuel_mass", self.fuel_mass)?; |         elem.set_item("fuel_mass", self.fuel_mass)?; | ||||||
|  |  | ||||||
							
								
								
									
										6
									
								
								setup.py
									
										
									
									
									
								
							
							
						
						
									
										6
									
								
								setup.py
									
										
									
									
									
								
							|  | @ -1,5 +1,5 @@ | ||||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||||
| from setuptools import find_packages, setup | from setuptools import find_packages, find_namespace_packages, setup | ||||||
| from setuptools_rust import Binding, RustExtension, Strip | from setuptools_rust import Binding, RustExtension, Strip | ||||||
| import os | import os | ||||||
| 
 | 
 | ||||||
|  | @ -68,7 +68,7 @@ setup( | ||||||
|     description="Elite: Dangerous long range route plotter", |     description="Elite: Dangerous long range route plotter", | ||||||
|     long_description=long_description, |     long_description=long_description, | ||||||
|     long_description_content_type="text/markdown", |     long_description_content_type="text/markdown", | ||||||
|     url="https://gitlab.com/Earthnuker/ed_lrr/-/tree/pyqt_gui", |     url="https://gitdab.com/Earthnuker/ED_LRR/src/branch/pyqt_gui", | ||||||
|     rust_extensions=[ |     rust_extensions=[ | ||||||
|         RustExtension( |         RustExtension( | ||||||
|             "_ed_lrr", |             "_ed_lrr", | ||||||
|  | @ -82,7 +82,7 @@ setup( | ||||||
|             quiet=True, |             quiet=True, | ||||||
|         ) |         ) | ||||||
|     ], |     ], | ||||||
|     packages=find_packages(), |     packages=find_namespace_packages(), | ||||||
|     entry_points={ |     entry_points={ | ||||||
|         "console_scripts": ["ed_lrr = ed_lrr_gui.__main__:main"], |         "console_scripts": ["ed_lrr = ed_lrr_gui.__main__:main"], | ||||||
|         "gui_scripts": ["ed_lrr_gui = ed_lrr_gui.__main__:gui_main"], |         "gui_scripts": ["ed_lrr_gui = ed_lrr_gui.__main__:gui_main"], | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue