1use super::ZXGraph;
4
5#[derive(Debug, Clone)]
8pub struct ZXLayerView {
9 z: i32,
10 node_ids: Vec<usize>,
11 spacelike_edges: Vec<(usize, usize)>,
12 past_timelike_nodes: Vec<usize>,
13 future_timelike_nodes: Vec<usize>,
14}
15
16impl ZXLayerView {
17 pub(crate) fn new(zx: &ZXGraph, z: i32) -> Self {
18 let mut node_ids = zx
19 .nodes
20 .iter()
21 .filter(|node| node.pos.z == z)
22 .map(|node| node.id)
23 .collect::<Vec<_>>();
24 node_ids.sort_unstable();
25
26 let mut spacelike_edges = zx
27 .edges
28 .iter()
29 .filter(|edge| {
30 edge.n1 < edge.n2 && zx.nodes[edge.n1].pos.z == z && zx.nodes[edge.n2].pos.z == z
31 })
32 .map(|edge| (edge.n1, edge.n2))
33 .collect::<Vec<_>>();
34 spacelike_edges.sort_unstable();
35
36 let mut past_timelike_nodes = Vec::new();
37 let mut future_timelike_nodes = Vec::new();
38 for &node_id in &node_ids {
39 let neighbors = zx.neighbor_ids(node_id);
40 if neighbors
41 .iter()
42 .any(|&neighbor| zx.nodes[neighbor].pos.z < z)
43 {
44 past_timelike_nodes.push(node_id);
45 }
46 if neighbors
47 .iter()
48 .any(|&neighbor| zx.nodes[neighbor].pos.z > z)
49 {
50 future_timelike_nodes.push(node_id);
51 }
52 }
53
54 Self {
55 z,
56 node_ids,
57 spacelike_edges,
58 past_timelike_nodes,
59 future_timelike_nodes,
60 }
61 }
62
63 pub fn z(&self) -> i32 {
65 self.z
66 }
67
68 pub fn node_ids(&self) -> &[usize] {
70 &self.node_ids
71 }
72
73 pub fn spacelike_edges(&self) -> &[(usize, usize)] {
75 &self.spacelike_edges
76 }
77
78 pub fn past_timelike_nodes(&self) -> &[usize] {
80 &self.past_timelike_nodes
81 }
82
83 pub fn future_timelike_nodes(&self) -> &[usize] {
85 &self.future_timelike_nodes
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 use glam::ivec3;
92 use rustc_hash::FxHashMap;
93
94 use crate::zx::graph::{CsrAdjacency, ZXEdge, ZXNode};
95 use crate::zx::{NodeKind, ZXGraph};
96
97 #[test]
98 fn zx_layer_view_exposes_exact_membership_and_edges() {
99 let zx = hand_built_zx_graph();
100 let layer = zx.layer(0);
101
102 let past = node_id(&zx, ivec3(0, 0, 0));
103 let left = node_id(&zx, ivec3(1, 0, 0));
104 let right = node_id(&zx, ivec3(2, 0, 0));
105 let isolated = node_id(&zx, ivec3(5, 5, 0));
106 let below = node_id(&zx, ivec3(0, 0, -1));
107
108 assert_eq!(zx.z_layers(), vec![-1, 0, 1]);
109 assert_eq!(layer.z(), 0);
110 assert_eq!(layer.node_ids(), &[past, left, right, isolated]);
111 assert_eq!(layer.spacelike_edges(), &[(left, right)]);
112 assert_eq!(layer.past_timelike_nodes(), &[past]);
113 assert_eq!(layer.future_timelike_nodes(), &[right]);
114
115 assert_eq!(
116 zx.edge_between(left, right)
117 .map(|edge| (edge.n1, edge.n2, edge.hadamard)),
118 Some((left, right, false))
119 );
120 assert_eq!(
121 zx.edge_between(right, left)
122 .map(|edge| (edge.n1, edge.n2, edge.hadamard)),
123 Some((right, left, false))
124 );
125 assert_eq!(
126 zx.edge_between(past, below)
127 .map(|edge| (edge.n1, edge.n2, edge.hadamard)),
128 Some((past, below, false))
129 );
130 assert_eq!(
131 zx.edge_between(below, past)
132 .map(|edge| (edge.n1, edge.n2, edge.hadamard)),
133 Some((below, past, false))
134 );
135 }
136
137 #[test]
138 fn zx_output_ports_ignore_isolated_ports() {
139 let zx = hand_built_zx_graph();
140
141 assert_eq!(zx.output_ports(), vec![glam::ivec3(0, 0, 0)]);
142 }
143
144 fn hand_built_zx_graph() -> crate::ZXGraph {
145 ZXGraph {
146 nodes: vec![
147 ZXNode::new(0, ivec3(0, 0, -1), NodeKind::X),
148 ZXNode::new(1, ivec3(0, 0, 0), NodeKind::Port),
149 ZXNode::new(2, ivec3(1, 0, 0), NodeKind::X),
150 ZXNode::new(3, ivec3(2, 0, 0), NodeKind::Z),
151 ZXNode::new(4, ivec3(2, 0, 1), NodeKind::X),
152 ZXNode::new(5, ivec3(5, 5, 0), NodeKind::Port),
153 ],
154 edges: vec![
155 ZXEdge {
156 n1: 0,
157 n2: 1,
158 id: 6,
159 hadamard: false,
160 },
161 ZXEdge {
162 n1: 1,
163 n2: 0,
164 id: 7,
165 hadamard: false,
166 },
167 ZXEdge {
168 n1: 2,
169 n2: 3,
170 id: 8,
171 hadamard: false,
172 },
173 ZXEdge {
174 n1: 3,
175 n2: 2,
176 id: 9,
177 hadamard: false,
178 },
179 ZXEdge {
180 n1: 3,
181 n2: 4,
182 id: 10,
183 hadamard: false,
184 },
185 ZXEdge {
186 n1: 4,
187 n2: 3,
188 id: 11,
189 hadamard: false,
190 },
191 ],
192 adjacency: CsrAdjacency::from_edges(
193 6,
194 &[
195 (0, 1, 6),
196 (1, 0, 7),
197 (2, 3, 8),
198 (3, 2, 9),
199 (3, 4, 10),
200 (4, 3, 11),
201 ],
202 ),
203 pos_to_node: FxHashMap::from_iter([
204 (ivec3(0, 0, -1), 0),
205 (ivec3(0, 0, 0), 1),
206 (ivec3(1, 0, 0), 2),
207 (ivec3(2, 0, 0), 3),
208 (ivec3(2, 0, 1), 4),
209 (ivec3(5, 5, 0), 5),
210 ]),
211 action_graph: crate::ActionDag::default(),
212 total_ids: 12,
213 cross_incident: Default::default(),
214 stabilizer_phase_basis: Default::default(),
215 }
216 }
217
218 fn node_id(zx: &ZXGraph, pos: glam::IVec3) -> usize {
219 zx.nodes
220 .iter()
221 .find(|node| node.pos == pos)
222 .map(|node| node.id)
223 .expect("node exists at position")
224 }
225}