vegas_lattice/
vertex.rs

1use crate::util::{python_mod, Axis, Tagged};
2use serde::{Deserialize, Serialize};
3use serde_json::Error as SerdeError;
4use std::str::FromStr;
5
6/// Represents a vertex in a lattice.
7///
8/// The `source` and `target` fields are the indices of the sites that the vertex connects.
9/// The `delta` field is a tuple of the displacements of the target site from the source site.
10/// A `delta` of `(0, 0, 1)` would mean that the target site is one unit along the z axis from the
11/// source site.
12///
13/// For a simple cubic lattice the `source` and `target` for all the vertices would be the same,
14/// `0`. While delta would be `(1, 0, 0)`, `(0, 1, 0)`, `(0, 0, 1)`. Those 3 vertices would connect
15/// all the sites in the lattice.
16///
17/// # Examples
18///
19/// Here is an example of how to create a vertex and access its fields:
20///
21/// ```rust
22/// use vegas_lattice::Vertex;
23///
24/// let vertex = Vertex::new(0, 1, (0, 0, 1));
25/// assert_eq!(vertex.source(), 0);
26/// assert_eq!(vertex.target(), 1);
27/// assert_eq!(vertex.delta(), (0, 0, 1));
28/// ```
29///
30/// Here's how to move a vertex along an axis:
31///
32/// ```rust
33/// use vegas_lattice::Vertex;
34///
35/// let vertex = Vertex::new(0, 1, (0, 0, 1)).move_z(1, 2, 3);
36/// assert_eq!(vertex.source(), 2);
37/// assert_eq!(vertex.target(), 5);
38/// assert_eq!(vertex.delta(), (0, 0, 0));
39/// ```
40#[derive(Clone, Debug, Serialize, Deserialize)]
41pub struct Vertex {
42    source: usize,
43    target: usize,
44    delta: (i32, i32, i32),
45    tags: Option<Vec<String>>,
46}
47
48impl FromStr for Vertex {
49    type Err = SerdeError;
50    fn from_str(source: &str) -> Result<Vertex, Self::Err> {
51        serde_json::from_str(source)
52    }
53}
54
55impl Tagged for Vertex {
56    fn tags(&self) -> Option<Vec<&str>> {
57        self.tags
58            .as_ref()
59            .map(|tags| tags.iter().map(|tag| tag.as_ref()).collect())
60    }
61}
62
63impl Vertex {
64    /// Creates a new vertex with the given source and target
65    pub fn new(source: usize, target: usize, delta: (i32, i32, i32)) -> Self {
66        Vertex {
67            source,
68            target,
69            delta,
70            tags: None,
71        }
72    }
73
74    /// Returns the `source` of the vertex
75    pub fn source(&self) -> usize {
76        self.source
77    }
78
79    /// Returns the `target` of the vertex
80    pub fn target(&self) -> usize {
81        self.target
82    }
83
84    /// Returns the `delta` of the vertex
85    pub fn delta(&self) -> (i32, i32, i32) {
86        self.delta
87    }
88
89    /// Chagges the tags of the vertex
90    pub fn with_tags(mut self, tags: Vec<&str>) -> Self {
91        self.tags = Some(tags.iter().map(|s| s.to_string()).collect());
92        self
93    }
94
95    #[inline]
96    fn delta_along(&self, axis: Axis) -> i32 {
97        match axis {
98            Axis::X => self.delta.0,
99            Axis::Y => self.delta.1,
100            Axis::Z => self.delta.2,
101        }
102    }
103
104    /// Move the vertex along a given axis by a given amount
105    ///
106    /// The only reason you would want to move a vertex along an axis is to grow a lattice along
107    /// that axis. That's why we're going to need to know the parameters of how much the lattice is
108    /// going to grow by. This function takes in the `nsites`, the number of sites in the original
109    /// lattice, and the `limit`, the total number of units the lattice will be grown by. The
110    /// vertex will be changed assuming that the new number of sites will be `limit * nsites`.
111    ///
112    /// # Warning
113    ///
114    /// This function is so complicated it should be *private*!
115    ///
116    /// # Arguments
117    ///
118    /// * `axis` - The axis along which to move the vertex
119    /// * `amount` - The number of units to move the vertex
120    /// * `nsites` - The number of sites in the original lattice
121    /// * `limit` - The total number of units the lattice will be grown by
122    fn move_along(mut self, axis: Axis, amount: usize, nsites: usize, limit: usize) -> Self {
123        debug_assert!(amount < limit);
124        let distance = amount * nsites;
125        let new_nsites = limit * nsites;
126        self.source += distance;
127        self.target += distance;
128        let delta = self.delta_along(axis);
129        let target = self.target as i32 + delta * nsites as i32;
130        let (target, delta) = python_mod(target, new_nsites);
131        self.target = target as usize;
132        match axis {
133            Axis::X => self.delta.0 = delta,
134            Axis::Y => self.delta.1 = delta,
135            Axis::Z => self.delta.2 = delta,
136        };
137        self
138    }
139
140    /// Move along the x axis
141    pub fn move_x(self, amount: usize, nsites: usize, limit: usize) -> Self {
142        self.move_along(Axis::X, amount, nsites, limit)
143    }
144
145    /// Move along the y axis
146    pub fn move_y(self, amount: usize, nsites: usize, limit: usize) -> Self {
147        self.move_along(Axis::Y, amount, nsites, limit)
148    }
149
150    /// Move along the z axis
151    pub fn move_z(self, amount: usize, nsites: usize, limit: usize) -> Self {
152        self.move_along(Axis::Z, amount, nsites, limit)
153    }
154
155    /// Re-index the vertex
156    pub fn reindex(mut self, index: &[usize]) -> Self {
157        self.source = index[self.source];
158        self.target = index[self.target];
159        self
160    }
161}
162
163#[cfg(test)]
164mod test {
165    use super::Vertex;
166
167    #[test]
168    fn vertex_can_be_created() {
169        let vertex = Vertex::new(0, 1, (0, 0, 1));
170        assert_eq!(vertex.source, 0);
171        assert_eq!(vertex.target, 1);
172        assert_eq!(vertex.delta, (0, 0, 1));
173    }
174
175    #[test]
176    fn vertex_can_be_moved() {
177        // The vertex would point to the second site in the next unit along the z axis
178        let vertex = Vertex::new(0, 1, (0, 0, 1)).move_along(super::Axis::Z, 0, 2, 2);
179        assert_eq!(vertex.source, 0);
180        assert_eq!(vertex.target, 3);
181        assert_eq!(vertex.delta, (0, 0, 0));
182    }
183
184    #[test]
185    fn vertex_can_be_moved_2() {
186        // The vertex would point to the second site on the same unit along the z axis
187        let vertex = Vertex::new(0, 1, (0, 0, 0)).move_along(super::Axis::Z, 0, 2, 2);
188        assert_eq!(vertex.source, 0);
189        assert_eq!(vertex.target, 1);
190        assert_eq!(vertex.delta, (0, 0, 0));
191    }
192
193    #[test]
194    fn vertex_can_be_moved_3() {
195        // The vertex would lay on the second unit along the z axis
196        let vertex = Vertex::new(0, 1, (0, 0, 0)).move_along(super::Axis::Z, 1, 2, 2);
197        assert_eq!(vertex.source, 2);
198        assert_eq!(vertex.target, 3);
199        assert_eq!(vertex.delta, (0, 0, 0));
200    }
201
202    #[test]
203    fn vertex_can_be_moved_4() {
204        // The vertex would point to the second site on the first unit along the z axis but in the
205        // next new delta.
206        let vertex = Vertex::new(0, 1, (0, 0, 1)).move_along(super::Axis::Z, 1, 2, 2);
207        assert_eq!(vertex.source, 2);
208        assert_eq!(vertex.target, 1);
209        assert_eq!(vertex.delta, (0, 0, 1));
210    }
211
212    #[test]
213    fn vertex_can_be_tagged() {
214        let vertex = Vertex::new(0, 1, (0, 0, 1)).with_tags(vec!["core"]);
215        assert_eq!(vertex.tags, Some(vec!["core".to_string()]));
216    }
217
218    #[test]
219    fn reindexing_kinda_works() {
220        let vertex = Vertex::new(0, 1, (0, 0, 1)).reindex(&[1, 0]);
221        assert_eq!(vertex.source, 1);
222        assert_eq!(vertex.target, 0);
223    }
224
225    #[test]
226    fn vertex_will_take_optional_tags() {
227        let data = r#"
228            {"source": 0, "target": 0, "delta": [0, 0, 1], "tags": ["core", "inner"]}
229        "#;
230        let vertex_result: Result<Vertex, _> = data.parse();
231        assert!(vertex_result.is_ok());
232        assert_eq!(
233            vertex_result.unwrap().tags,
234            Some(vec!["core".to_string(), "inner".to_string()])
235        );
236    }
237}