Skip to content

Commit

Permalink
feat: add sfm-designer-solver
Browse files Browse the repository at this point in the history
  • Loading branch information
adamchristiansen committed Sep 19, 2024
1 parent 6aad0f3 commit 1298d06
Show file tree
Hide file tree
Showing 9 changed files with 469 additions and 22 deletions.
1 change: 1 addition & 0 deletions sfm-designer-solver/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/target
25 changes: 25 additions & 0 deletions sfm-designer-solver/Cargo.lock

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

11 changes: 11 additions & 0 deletions sfm-designer-solver/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "sfm-designer-solver"
version = "0.1.0"
edition = "2021"
publish = false

[dependencies]
itertools = "0.13.0"

[profile.release]
lto = true
16 changes: 16 additions & 0 deletions sfm-designer-solver/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# SFM Interferometer Designer Solver

This is a solver used to generate the interferometer configurations for the app.
Finding solutions is computationally hard, so these are pregenerated using an
efficient Rust program.

The solutions are generated in a format that can simply be pasted into the
[Configuration.jsx](../src/model/Configuration.jsx). To generate the solutions,
run

```sh
cargo run --release
```

using the `--release` flag is recommended since it is over 10 times faster
than debug mode.
133 changes: 133 additions & 0 deletions sfm-designer-solver/src/int_set.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
use std::ops::Index;

/// A set of integers implemented as a sorted vector.
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub(crate) struct IntSet(Vec<usize>);

impl IntSet {
/// Create a new set from the given values. Duplicates are removed.
pub(crate) fn from(mut values: Vec<usize>) -> Self {
values.sort();
values.dedup();
Self(values)
}

/// The number of items in the set.
pub(crate) fn len(&self) -> usize {
self.0.len()
}

/// Get the items as a slice.
pub(crate) fn as_slice(&self) -> &[usize] {
self.0.as_slice()
}

/// Iterate over the items in ascending order.
pub(crate) fn iter(&self) -> std::slice::Iter<usize> {
self.as_slice().iter()
}
}

impl Index<usize> for IntSet {
type Output = usize;

fn index(&self, index: usize) -> &Self::Output {
&self.0[index]
}
}

/// A multiset of integers implemented as a sorted vector.
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub(crate) struct IntMultiset(Vec<usize>);

impl IntMultiset {
/// Create a new set from the given values.
pub(crate) fn from(mut values: Vec<usize>) -> Self {
values.sort();
Self(values)
}

/// The number of items in the set including duplicates.
pub(crate) fn len(&self) -> usize {
self.0.len()
}

/// Test whether this is a set, meaning there are no duplicates.
pub(crate) fn is_set(&self) -> bool {
// Since the values are sorted only adjacent values need to be compared.
for i in 1..self.len() {
if self.0[i] == self.0[i - 1] {
return false;
}
}
true
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn int_set_from() {
assert_eq!(IntSet::from(vec![]), IntSet(vec![]));
assert_eq!(IntSet::from(vec![1, 2, 3]), IntSet(vec![1, 2, 3]));
assert_eq!(IntSet::from(vec![1, 2, 3, 2, 1]), IntSet(vec![1, 2, 3]));
}

#[test]
fn int_set_len() {
assert_eq!(0, IntSet::from(vec![]).len());
assert_eq!(3, IntSet::from(vec![1, 2, 3]).len());
assert_eq!(3, IntSet::from(vec![1, 2, 3, 2, 1]).len());
}

#[test]
fn int_set_as_slice() {
let set = IntSet::from(vec![1, 2, 3]);
let slice = set.as_slice();
assert_eq!(slice, [1, 2, 3]);
}

#[test]
fn int_set_iter() {
let set = IntSet::from(vec![1, 2, 3]);
let mut it = set.iter();
assert_eq!(it.next(), Some(1).as_ref());
assert_eq!(it.next(), Some(2).as_ref());
assert_eq!(it.next(), Some(3).as_ref());
assert_eq!(it.next(), None);
}

#[test]
fn int_set_index() {
let set = IntSet::from(vec![1, 2, 5]);
assert_eq!(set[0], 1);
assert_eq!(set[1], 2);
assert_eq!(set[2], 5);
}

#[test]
fn intmultiset_from() {
assert_eq!(IntMultiset::from(vec![]), IntMultiset(vec![]));
assert_eq!(IntMultiset::from(vec![1, 2, 3]), IntMultiset(vec![1, 2, 3]));
assert_eq!(
IntMultiset::from(vec![1, 2, 3, 2, 1]),
IntMultiset(vec![1, 1, 2, 2, 3])
);
}

#[test]
fn int_multiset_len() {
assert_eq!(0, IntMultiset::from(vec![]).len());
assert_eq!(3, IntMultiset::from(vec![1, 2, 3]).len());
assert_eq!(5, IntMultiset::from(vec![1, 2, 3, 2, 1]).len());
}

#[test]
fn int_multiset_is_set() {
assert!(IntMultiset::from(vec![]).is_set());
assert!(IntMultiset::from(vec![1, 2, 3]).is_set());
assert!(!IntMultiset::from(vec![1, 2, 3, 2, 1]).is_set());
}
}
38 changes: 38 additions & 0 deletions sfm-designer-solver/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
mod int_set;
mod model;

use model::Solutions;

fn main() {
let max_size = 10;
let mut largest = 1;
println!("// DO NOT EDIT.");
println!("// This file was automatically generated by sfm-designer-solver.");
println!("//");
println!("// Each key in the solution is the number of reflections in the interferometer.");
println!("const SOLUTIONS = {{");
for size in 2..=max_size {
let solutions = Solutions::find_starting_at(size, largest);
println!(" {}: [", size);
solutions.iter().enumerate().for_each(|(index, lengths)| {
println!(
" {{ value: {}, solution: {:?}, name: '{:?}' }},",
index,
lengths.lengths().as_slice(),
lengths.lengths().as_slice(),
)
});
println!(" ],");
if let Some(new_largest) = solutions
.iter()
.filter_map(|lengths| lengths.lengths().iter().max())
.max()
{
largest = *new_largest;
}
largest = largest.max(size);
}
println!("}};");
println!();
println!("export default SOLUTIONS;");
}
Loading

0 comments on commit 1298d06

Please sign in to comment.