use crate::types::{ColorCount, ColorIdx, Distance, PointCount, PointIdx};
#[derive(Debug, PartialOrd, PartialEq, Eq, Hash)]
pub struct Point {
index: PointIdx,
}
impl Point {
pub fn idx(&self) -> PointIdx {
self.index
}
}
pub trait ColoredMetric {
fn dist(&self, x1: &Point, x2: &Point) -> Distance;
fn color(&self, x: &Point) -> ColorIdx;
fn dist_set(&self, x: &Point, point_set: Vec<&Point>) -> Distance {
let mut current_distance = Distance::MAX;
for p in point_set {
let d = self.dist(x, p);
if d < current_distance {
current_distance = d;
}
}
current_distance
}
fn get_closest<'a>(&self, x: &Point, point_set: &Vec<&'a Point>) -> (Distance, &'a Point) {
assert!(
!point_set.is_empty(),
"there has to be at least one point in the point set."
);
let mut current_distance = Distance::MAX;
let mut current_closest: &Point = point_set[0];
for p in point_set {
let d = self.dist(x, p);
if d < current_distance {
current_distance = d;
current_closest = p;
}
}
(current_distance, current_closest)
}
fn n(&self) -> PointCount;
fn point_iter(&self) -> std::slice::Iter<Point>;
fn get_point(&self, idx: PointIdx) -> Option<&Point>;
fn gamma(&self) -> ColorCount;
fn is_metric(&self) -> bool {
for x in self.point_iter() {
for y in self.point_iter() {
let dist_xy = self.dist(x, y);
let dist_yx = self.dist(y, x);
if dist_xy != dist_yx {
println!(
"Symmetry is violated: x={:?}, y={:?}, dist(x,y)={}, dist(y,x)={}",
*x, *y, dist_xy, dist_yx
);
return false;
}
if x == y && dist_xy != 0.0 {
println!(
"Identity of indiscernibles is violated: x={:?}, dist(x,x)={}",
*x, dist_xy
);
return false;
}
if x != y && dist_xy <= 0.0 {
println!("Non-negativity or identity of indiscernibles is violated: x={:?}, y={:?}, dist(x,y)={}", *x, *y, dist_xy);
return false;
}
}
}
for x in self.point_iter() {
for y in self.point_iter() {
let dist_xy = self.dist(x, y);
for z in self.point_iter() {
let dist_xz = self.dist(x, z);
let dist_zy = self.dist(z, y);
if dist_xy > dist_xz + dist_zy {
println!("Triangle inequality is violated: x={:?}, y={:?}, z={:?}, dist(x,y)={}, dist(x,z)={}, dist(z,y)={}, i.e., {} > {}", *x, *y, *z, dist_xy, dist_xz, dist_zy, dist_xy, dist_xz + dist_zy);
return false;
}
}
}
}
true
}
}
pub struct SpaceMatrix {
distances: Vec<Vec<Distance>>, points: Vec<Point>,
colors: Vec<ColorIdx>, gamma: ColorCount,
}
impl SpaceMatrix {
pub fn new(distances: Vec<Vec<Distance>>, colors: Vec<ColorIdx>) -> SpaceMatrix {
let number_of_rows = distances.len();
let mut points: Vec<Point> = Vec::with_capacity(number_of_rows);
for (i, row) in distances.iter().enumerate() {
assert_eq!(
row.len(),
number_of_rows,
"Matrix is not quadratic. row {} has {} entries; number of rows: {}",
i,
row.len(),
number_of_rows
);
points.push(Point { index: i });
}
assert_eq!(
number_of_rows,
colors.len(),
"Number of points: {} does not match number of colors: {}",
number_of_rows,
colors.len()
);
let gamma = colors.iter().max().expect("No maximal color found") + 1;
let space = SpaceMatrix {
distances,
colors,
points,
gamma,
};
assert!(
space.is_metric(),
"Distances do not satisfy the metric properties."
);
space
}
pub fn new_by_array<const N: PointCount>(
distances: [[Distance; N]; N],
colors: [ColorIdx; N],
) -> SpaceMatrix {
let mut points: Vec<Point> = Vec::with_capacity(N);
for i in 0..N {
points.push(Point { index: i });
}
let distances: Vec<Vec<Distance>> = distances.iter().map(|row| row.to_vec()).collect();
let colors = colors.to_vec();
let gamma = colors.iter().max().expect("No maximal color found") + 1;
SpaceMatrix {
distances,
colors,
points,
gamma,
}
}
}
impl ColoredMetric for SpaceMatrix {
fn dist(&self, x1: &Point, x2: &Point) -> Distance {
self.distances[x1.idx()][x2.idx()]
}
fn color(&self, x: &Point) -> ColorIdx {
self.colors[x.idx()]
}
fn n(&self) -> PointCount {
self.points.len()
}
fn get_point(&self, idx: PointIdx) -> Option<&Point> {
if idx >= self.points.len() {
return None;
}
Some(&self.points[idx])
}
fn point_iter(&self) -> std::slice::Iter<Point> {
self.points.iter()
}
fn gamma(&self) -> ColorCount {
self.gamma
}
}
type Position = Vec<Distance>;
pub struct SpaceND {
points: Vec<Point>,
positions: Vec<Position>,
colors: Vec<ColorIdx>,
gamma: ColorCount,
}
use rand::Rng;
use std::fs::File;
use std::io::{BufRead, BufReader};
impl SpaceND {
pub fn by_ndpoints(positions: Vec<Position>, colors: Vec<ColorIdx>) -> SpaceND {
assert!(
!positions.is_empty(),
"There need to be at least one position given."
);
assert_eq!(
positions.len(),
colors.len(),
"The number of points in position must equal the number of colors!"
);
let gamma = colors.iter().max().expect("No maximal color found") + 1;
let dimension = positions[0].len();
(1..positions.len()).for_each(|i| {
assert_eq!(
positions[i].len(),
dimension,
"Dimension is {}, but positions[{}] has only {} entries",
dimension,
i,
positions[i].len()
);
});
SpaceND {
points: (0..positions.len()).map(|i| Point { index: i }).collect(),
positions,
colors,
gamma,
}
}
pub fn new_random(n: PointCount) -> SpaceND {
let mut rng = rand::thread_rng();
let positions = (0..n)
.map(|_| {
vec![
rng.gen_range(-100.0f32..100.0f32),
rng.gen_range(-100.0f32..100.0f32),
]
})
.collect();
let colors = (0..n).map(|_| rng.gen_range(0..10)).collect();
SpaceND::by_ndpoints(positions, colors)
}
pub fn by_file(file_path: &str, expected_number_of_points: PointCount, verbose: u8) -> SpaceND {
let f = File::open(file_path).expect("Cannot open file to read ndpoints.");
let f = BufReader::new(f);
let mut positions: Vec<Position> = Vec::with_capacity(expected_number_of_points);
let mut colors: Vec<ColorIdx> = Vec::with_capacity(expected_number_of_points);
let mut dim = 0;
for line in f.lines() {
let content = line.unwrap();
let content: Vec<&str> = content.split(',').collect();
if dim == 0 {
dim = content.len() - 1;
} else {
assert_eq!(
dim,
content.len() - 1,
"Line {} has the wrong number of entries.",
positions.len()
);
}
positions.push(
(0..dim)
.map(|i| {
content[i].parse::<Distance>().unwrap_or_else(|_| {
panic!(
"Cannot parse entry {} to f32 on line {}.",
i,
positions.len()
)
})
})
.collect(),
);
colors.push(content[dim].parse::<ColorIdx>().unwrap_or_else(|_| {
panic!("Cannot parse color-entry to u16 on line {}", colors.len())
}));
}
if verbose >= 1 {
println!(
"\n**** Successfully loaded {} points/colors (dimension: {}) from '{}'",
positions.len(),
dim,
file_path
);
}
#[cfg(debug_assertions)]
{
print!(" positions:");
let mut counter = 0;
positions.iter().for_each(|p| {
if counter % 10 == 0 {
print!(
"\n\t{}-{}:\t",
counter,
<usize>::min(counter + 9, positions.len())
);
}
print!("{:?} ", p);
counter += 1;
});
println!("\n colors: {:?}", colors);
}
let gamma = colors.iter().max().expect("No maximal color found") + 1;
SpaceND {
points: (0..positions.len()).map(|i| Point { index: i }).collect(),
positions,
colors,
gamma,
}
}
pub(crate) fn get_positions(&self) -> Vec<Position> {
self.positions.clone()
}
pub(crate) fn get_colors(&self) -> Vec<ColorIdx> {
self.colors.clone()
}
}
impl ColoredMetric for SpaceND {
fn dist(&self, x1: &Point, x2: &Point) -> Distance {
let dim = self.positions[x1.idx()].len();
let d_squared: Distance = (0..dim)
.map(|i| {
(self.positions[x1.idx()][i] - self.positions[x2.idx()][i])
* (self.positions[x1.idx()][i] - self.positions[x2.idx()][i])
})
.sum();
d_squared.sqrt()
}
fn color(&self, x: &Point) -> ColorIdx {
self.colors[x.idx()]
}
fn get_point(&self, idx: PointIdx) -> Option<&Point> {
if idx >= self.points.len() {
return None;
}
Some(&self.points[idx])
}
fn point_iter(&self) -> std::slice::Iter<Point> {
self.points.iter()
}
fn n(&self) -> PointCount {
self.points.len()
}
fn gamma(&self) -> ColorCount {
self.gamma
}
}