1use std::fmt;
2use std::ops::Index;
3use std::sync::Arc;
4
5use petgraph::Direction;
6use petgraph::stable_graph::{NodeIndex, StableDiGraph};
7use petgraph::visit::{EdgeRef, IntoEdgeReferences, Visitable};
8
9use super::{BloqEdge, BloqNode, BloqNodeId, ClassicalExpr, ClassicalNode, QuantumNode, ValueRole};
10
11mod classical_data;
12
13#[derive(Debug, Clone, Default)]
23pub struct SubGraph {
24 data: Arc<SubGraphData>,
25}
26
27#[derive(Debug, Clone, Default)]
28struct SubGraphData {
29 graph: StableDiGraph<BloqNode, BloqEdge>,
30 value_output: Option<super::ValueRef>,
31 boundary_outputs: Vec<BloqNodeId>,
32}
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36pub(crate) enum LevelRebuildError {
37 DuplicateNode(u32),
38 MissingEndpoint {
40 index: usize,
41 id: u32,
42 },
43 ExcessiveVacancy {
48 holes: usize,
49 live: usize,
50 },
51}
52
53impl fmt::Display for LevelRebuildError {
54 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55 match self {
56 Self::DuplicateNode(id) => write!(f, "duplicate node n{id}"),
57 Self::MissingEndpoint { id, .. } => {
58 write!(f, "edge endpoint n{id} is not a live node")
59 }
60 Self::ExcessiveVacancy { holes, live } => write!(
61 f,
62 "node ids are implausibly sparse: {holes} vacant slots for {live} live nodes"
63 ),
64 }
65 }
66}
67
68const MAX_EXTRA_VACANCIES: usize = 1 << 16;
73
74pub(crate) fn rebuild_level(
88 mut nodes: Vec<(u32, BloqNode)>,
89 edges: Vec<(u32, u32, BloqEdge)>,
90) -> Result<SubGraph, LevelRebuildError> {
91 nodes.sort_by_key(|(id, _)| *id);
92 if let Some(window) = nodes.windows(2).find(|window| window[0].0 == window[1].0) {
93 return Err(LevelRebuildError::DuplicateNode(window[0].0));
94 }
95 let bound = nodes.last().map_or(0, |(id, _)| u64::from(*id) + 1);
96 let live = nodes.len();
97 let holes = bound - live as u64;
98 let vacancy_error = || LevelRebuildError::ExcessiveVacancy {
99 holes: usize::try_from(holes).unwrap_or(usize::MAX),
100 live,
101 };
102 if holes
103 > (live as u64)
104 .saturating_mul(4)
105 .saturating_add(MAX_EXTRA_VACANCIES as u64)
106 {
107 return Err(vacancy_error());
108 }
109 let bound = usize::try_from(bound).map_err(|_| vacancy_error())?;
110
111 let mut level = SubGraph::new();
112 level.graph_mut().reserve_exact_nodes(bound);
113 let mut pending = nodes.into_iter().peekable();
114 let mut placeholders = Vec::new();
115 for slot in 0..bound {
116 if pending.peek().is_some_and(|(id, _)| *id as usize == slot) {
117 let (_, node) = pending.next().expect("peek said so");
118 level.add_node(node);
119 } else {
120 placeholders.push(level.add_node(BloqNode::classical(ClassicalNode::Compute {
121 expr: ClassicalExpr::Const(false),
122 })));
123 }
124 }
125 drop(pending);
126 for placeholder in placeholders {
127 level
128 .graph_mut()
129 .remove_node(to_index(placeholder))
130 .expect("placeholder was just added");
131 }
132
133 for (index, (source, target, _)) in edges.iter().enumerate() {
134 check_edge_endpoints(&level, index, *source, *target)?;
135 }
136 level.graph_mut().reserve_exact_edges(edges.len());
137 for (source, target, edge) in edges {
138 level.add_edge(BloqNodeId(source), BloqNodeId(target), edge);
139 }
140 Ok(level)
141}
142
143fn check_edge_endpoints(
144 level: &SubGraph,
145 index: usize,
146 source: u32,
147 target: u32,
148) -> Result<(), LevelRebuildError> {
149 for id in [source, target] {
150 if level.node(BloqNodeId(id)).is_none() {
151 return Err(LevelRebuildError::MissingEndpoint { index, id });
152 }
153 }
154 Ok(())
155}
156
157#[derive(serde::Serialize)]
162struct SubGraphWireRef<'a> {
163 #[serde(serialize_with = "serialize_nodes")]
164 nodes: &'a SubGraph,
165 #[serde(serialize_with = "serialize_edges")]
166 edges: &'a SubGraph,
167 value_output: Option<super::ValueRef>,
168 boundary_outputs: &'a [BloqNodeId],
169}
170
171fn serialize_nodes<S: serde::Serializer>(
174 graph: &SubGraph,
175 serializer: S,
176) -> Result<S::Ok, S::Error> {
177 use serde::ser::SerializeSeq;
178 let mut sequence = serializer.serialize_seq(Some(graph.node_count()))?;
179 for (id, node) in graph.nodes() {
180 sequence.serialize_element(&(id.0, node))?;
181 }
182 sequence.end()
183}
184
185fn serialize_edges<S: serde::Serializer>(
186 graph: &SubGraph,
187 serializer: S,
188) -> Result<S::Ok, S::Error> {
189 use serde::ser::SerializeSeq;
190 if !serializer.is_human_readable() {
191 return edge_runs::serialize(graph, serializer);
192 }
193 let mut sequence = serializer.serialize_seq(Some(graph.edge_count()))?;
194 for edge in graph.edges() {
195 sequence.serialize_element(&(edge.source.0, edge.target.0, edge.edge))?;
196 }
197 sequence.end()
198}
199
200#[derive(serde::Deserialize)]
201struct SubGraphWire {
202 nodes: Vec<(u32, BloqNode)>,
203 edges: Vec<(u32, u32, BloqEdge)>,
204 value_output: Option<super::ValueRef>,
205 boundary_outputs: Vec<BloqNodeId>,
206}
207
208mod edge_runs {
211 use super::{BloqEdge, BloqNodeId, SubGraph, ValueRole, check_edge_endpoints};
212 use crate::{ObservableOutput, QuantumEdge};
213 use serde::de::{DeserializeSeed, EnumAccess, Error, SeqAccess, VariantAccess, Visitor};
214 use serde::ser::SerializeSeq;
215
216 #[derive(serde::Serialize)]
217 enum Edge<'a> {
218 Quantum(u32, u32, &'a QuantumEdge),
219 Value(u32, u32, u32, &'a ValueRole, ObservableOutput),
220 Compose(u32, u32, u32, &'a ValueRole),
221 Order(u32, u32),
222 ValueRun(u32, u32, &'a ValueRole, ObservableOutput, &'a [u32]),
223 }
224
225 #[derive(serde::Deserialize)]
226 #[serde(field_identifier)]
227 enum EdgeKind {
228 Quantum,
229 Value,
230 Compose,
231 Order,
232 ValueRun,
233 }
234
235 pub(super) fn serialize<S: serde::Serializer>(
236 graph: &SubGraph,
237 serializer: S,
238 ) -> Result<S::Ok, S::Error> {
239 let mut count = 0usize;
240 let mut previous: Option<(crate::BloqNodeId, u32, &ValueRole, ObservableOutput)> = None;
241 for edge in graph.edges() {
242 let next = match edge.edge {
243 BloqEdge::Value { slot, role, output } => Some((edge.target, *slot, role, *output)),
244 _ => None,
245 };
246 let continues = match (previous, next) {
247 (
248 Some((target, slot, role, output)),
249 Some((next_target, next_slot, next_role, next_output)),
250 ) => {
251 target == next_target
252 && role == next_role
253 && output == next_output
254 && slot.checked_add(1) == Some(next_slot)
255 }
256 _ => false,
257 };
258 count += usize::from(!continues);
259 previous = next;
260 }
261 let mut sequence = serializer.serialize_seq(Some(count))?;
262 let mut edges = graph.edges().peekable();
263 let mut sources = Vec::new();
265 while let Some(edge) = edges.next() {
266 let encoded = match edge.edge {
267 BloqEdge::Quantum(value) => Edge::Quantum(edge.source.0, edge.target.0, value),
268 BloqEdge::Order => Edge::Order(edge.source.0, edge.target.0),
269 BloqEdge::Compose { slot, role } => {
270 Edge::Compose(edge.source.0, edge.target.0, *slot, role)
271 }
272 BloqEdge::Value { slot, role, output } => {
273 sources.clear();
274 sources.push(edge.source.0);
275 while let Some(next) = edges.peek() {
276 let BloqEdge::Value {
277 slot: next_slot,
278 role: next_role,
279 output: next_output,
280 } = next.edge
281 else {
282 break;
283 };
284 if next.target != edge.target
285 || next_role != role
286 || next_output != output
287 || u32::try_from(sources.len())
288 .ok()
289 .and_then(|length| slot.checked_add(length))
290 != Some(*next_slot)
291 {
292 break;
293 }
294 sources.push(edges.next().expect("peeked edge").source.0);
295 }
296 if sources.len() == 1 {
297 Edge::Value(edge.source.0, edge.target.0, *slot, role, *output)
298 } else {
299 Edge::ValueRun(edge.target.0, *slot, role, *output, &sources)
300 }
301 }
302 };
303 sequence.serialize_element(&encoded)?;
304 }
305 sequence.end()
306 }
307
308 fn add_edge<E: Error>(
309 level: &mut SubGraph,
310 source: u32,
311 target: u32,
312 edge: BloqEdge,
313 ) -> Result<(), E> {
314 check_edge_endpoints(level, level.edge_count(), source, target).map_err(E::custom)?;
315 level.add_edge(BloqNodeId(source), BloqNodeId(target), edge);
316 Ok(())
317 }
318
319 pub(super) struct EdgeSequence<'a>(pub(super) &'a mut SubGraph);
320 impl<'de> DeserializeSeed<'de> for EdgeSequence<'_> {
321 type Value = ();
322 fn deserialize<D: serde::Deserializer<'de>>(self, deserializer: D) -> Result<(), D::Error> {
323 deserializer.deserialize_seq(self)
324 }
325 }
326 impl<'de> Visitor<'de> for EdgeSequence<'_> {
327 type Value = ();
328 fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
329 formatter.write_str("graph edges and consecutive value-edge runs")
330 }
331 fn visit_seq<A: SeqAccess<'de>>(self, mut sequence: A) -> Result<(), A::Error> {
332 let capacity = sequence
334 .size_hint()
335 .unwrap_or(0)
336 .min((1 << 20) / size_of::<petgraph::graph::Edge<Option<BloqEdge>>>());
337 self.0.graph_mut().reserve_exact_edges(capacity);
338 while sequence.next_element_seed(EdgeSeed(self.0))?.is_some() {}
339 self.0.graph_mut().shrink_to_fit_edges();
340 Ok(())
341 }
342 }
343
344 struct EdgeSeed<'a>(&'a mut SubGraph);
345 impl<'de> DeserializeSeed<'de> for EdgeSeed<'_> {
346 type Value = ();
347 fn deserialize<D: serde::Deserializer<'de>>(self, deserializer: D) -> Result<(), D::Error> {
348 deserializer.deserialize_enum(
349 "Edge",
350 &["Quantum", "Value", "Compose", "Order", "ValueRun"],
351 self,
352 )
353 }
354 }
355 impl<'de> Visitor<'de> for EdgeSeed<'_> {
356 type Value = ();
357 fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
358 formatter.write_str("one graph edge or value-edge run")
359 }
360 fn visit_enum<A: EnumAccess<'de>>(self, data: A) -> Result<(), A::Error> {
361 let (kind, value) = data.variant::<EdgeKind>()?;
362 match kind {
363 EdgeKind::Quantum => {
364 let (source, target, edge) =
365 value.newtype_variant::<(u32, u32, QuantumEdge)>()?;
366 add_edge::<A::Error>(
367 self.0,
368 source,
369 target,
370 BloqEdge::Quantum(Box::new(edge)),
371 )?;
372 }
373 EdgeKind::Value => {
374 let (source, target, slot, role, output) =
375 value.newtype_variant::<(u32, u32, u32, ValueRole, ObservableOutput)>()?;
376 add_edge::<A::Error>(
377 self.0,
378 source,
379 target,
380 BloqEdge::Value { slot, role, output },
381 )?;
382 }
383 EdgeKind::Compose => {
384 let (source, target, slot, role) =
385 value.newtype_variant::<(u32, u32, u32, ValueRole)>()?;
386 add_edge::<A::Error>(self.0, source, target, BloqEdge::Compose { slot, role })?;
387 }
388 EdgeKind::Order => {
389 let (source, target) = value.newtype_variant::<(u32, u32)>()?;
390 add_edge::<A::Error>(self.0, source, target, BloqEdge::Order)?;
391 }
392 EdgeKind::ValueRun => return value.tuple_variant(5, Run(self.0)),
393 }
394 Ok(())
395 }
396 }
397
398 struct Run<'a>(&'a mut SubGraph);
399 impl<'de> Visitor<'de> for Run<'_> {
400 type Value = ();
401 fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
402 formatter.write_str("target, first slot, role, output and source IDs")
403 }
404 fn visit_seq<A: SeqAccess<'de>>(self, mut sequence: A) -> Result<(), A::Error> {
405 let target = sequence
406 .next_element()?
407 .ok_or_else(|| A::Error::custom("value run needs a target"))?;
408 let slot = sequence
409 .next_element()?
410 .ok_or_else(|| A::Error::custom("value run needs a slot"))?;
411 let role = sequence
412 .next_element()?
413 .ok_or_else(|| A::Error::custom("value run needs a role"))?;
414 let output = sequence
415 .next_element()?
416 .ok_or_else(|| A::Error::custom("value run needs an output"))?;
417 sequence
418 .next_element_seed(Sources {
419 edges: self.0,
420 target,
421 slot,
422 role,
423 output,
424 })?
425 .ok_or_else(|| A::Error::custom("value run needs source IDs"))
426 }
427 }
428
429 struct Sources<'a> {
430 edges: &'a mut SubGraph,
431 target: u32,
432 slot: u32,
433 role: ValueRole,
434 output: ObservableOutput,
435 }
436 impl<'de> DeserializeSeed<'de> for Sources<'_> {
437 type Value = ();
438 fn deserialize<D: serde::Deserializer<'de>>(self, deserializer: D) -> Result<(), D::Error> {
439 deserializer.deserialize_seq(self)
440 }
441 }
442 impl<'de> Visitor<'de> for Sources<'_> {
443 type Value = ();
444 fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
445 formatter.write_str("at least two source IDs with consecutive slots")
446 }
447 fn visit_seq<A: SeqAccess<'de>>(self, mut sequence: A) -> Result<(), A::Error> {
448 let mut count = 0usize;
449 while let Some(source) = sequence.next_element::<u32>()? {
450 let offset = u32::try_from(count)
451 .map_err(|_| A::Error::custom("value run slot overflow"))?;
452 let slot = self
453 .slot
454 .checked_add(offset)
455 .ok_or_else(|| A::Error::custom("value run slot overflow"))?;
456 add_edge::<A::Error>(
459 self.edges,
460 source,
461 self.target,
462 BloqEdge::Value {
463 slot,
464 role: self.role.clone(),
465 output: self.output,
466 },
467 )?;
468 count += 1;
469 }
470 if count < 2 {
471 return Err(A::Error::custom("value run needs at least two sources"));
472 }
473 Ok(())
474 }
475 }
476
477 #[cfg(test)]
478 mod tests {
479 use super::*;
480 use crate::{BloqNode, ClassicalExpr, ClassicalNode, NodeProvenance, ValueRef};
481
482 #[derive(Clone, serde::Serialize)]
483 enum WireKind {
484 #[allow(
486 dead_code,
487 reason = "the wire tag must match the production quantum variant"
488 )]
489 Quantum,
490 Classical(u32),
491 }
492
493 #[derive(Clone, serde::Serialize)]
494 struct WireNode {
495 kind: WireKind,
496 provenance: NodeProvenance,
497 activation: Option<u32>,
498 }
499
500 struct Decoded(SubGraph);
501
502 impl<'de> serde::Deserialize<'de> for Decoded {
503 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
504 let mut graph = SubGraph::new();
505 for _ in 0..10 {
506 graph.add_node(BloqNode::classical(ClassicalNode::Compute {
507 expr: ClassicalExpr::Const(false),
508 }));
509 }
510 EdgeSequence(&mut graph).deserialize(deserializer)?;
511 Ok(Self(graph))
512 }
513 }
514
515 fn encode(value: &impl serde::Serialize) -> Vec<u8> {
516 postcard::to_extend(value, Vec::new()).unwrap()
517 }
518
519 #[test]
520 fn value_runs_preserve_full_width_slots_duplicates_roles_and_edge_order() {
521 let role = ValueRole::FeedbackFold { action: u32::MAX };
522 let source = [9, 9];
523 let encoded = encode(&vec![
524 Edge::Value(1, 2, 7, &ValueRole::Data, ObservableOutput::Corrected),
525 Edge::Order(2, 3),
526 Edge::ValueRun(9, u32::MAX - 1, &role, ObservableOutput::Flip, &source),
527 Edge::Value(
528 3,
529 9,
530 0,
531 &ValueRole::ReadoutFold,
532 ObservableOutput::Corrected,
533 ),
534 ]);
535 let expected = vec![
536 (1, 2, BloqEdge::value(7)),
537 (2, 3, BloqEdge::Order),
538 (
539 9,
540 9,
541 BloqEdge::Value {
542 slot: u32::MAX - 1,
543 role: role.clone(),
544 output: ObservableOutput::Flip,
545 },
546 ),
547 (
548 9,
549 9,
550 BloqEdge::Value {
551 slot: u32::MAX,
552 role,
553 output: ObservableOutput::Flip,
554 },
555 ),
556 (
557 3,
558 9,
559 BloqEdge::Value {
560 slot: 0,
561 role: ValueRole::ReadoutFold,
562 output: ObservableOutput::Corrected,
563 },
564 ),
565 ];
566 assert_eq!(
567 postcard::from_bytes::<Decoded>(&encoded)
568 .unwrap()
569 .0
570 .edges()
571 .map(|edge| (edge.source.0, edge.target.0, edge.edge.clone()))
572 .collect::<Vec<_>>(),
573 expected
574 );
575 for end in 0..encoded.len() {
576 assert!(postcard::from_bytes::<Decoded>(&encoded[..end]).is_err());
577 }
578 }
579
580 #[test]
581 fn value_run_decoder_rejects_invalid_lengths_slots_and_endpoints() {
582 for (slot, sources) in [(0, vec![]), (0, vec![0]), (u32::MAX, vec![0, 1])] {
583 let encoded = encode(&vec![Edge::ValueRun(
584 0,
585 slot,
586 &ValueRole::Data,
587 ObservableOutput::Flip,
588 &sources,
589 )]);
590 assert!(postcard::from_bytes::<Decoded>(&encoded).is_err());
591 }
592 for encoded in [
594 encode(&usize::MAX),
595 encode(&(
596 1usize,
597 4u32,
598 0u32,
599 0u32,
600 ValueRole::Data,
601 ObservableOutput::Flip,
602 usize::MAX,
603 )),
604 encode(&(1usize, 5u32)),
605 ] {
606 assert!(postcard::from_bytes::<Decoded>(&encoded).is_err());
607 }
608 #[derive(serde::Serialize)]
609 struct InvalidLevel<'a> {
610 classical: Vec<ClassicalNode>,
611 nodes: Vec<(u32, WireNode)>,
612 edges: Vec<Edge<'a>>,
613 value_output: Option<ValueRef>,
614 boundary_outputs: Vec<BloqNodeId>,
615 }
616 let mut wire = InvalidLevel {
617 classical: vec![ClassicalNode::Compute {
618 expr: ClassicalExpr::Const(false),
619 }],
620 nodes: vec![(
621 0,
622 WireNode {
623 kind: WireKind::Classical(0),
624 provenance: NodeProvenance::None,
625 activation: None,
626 },
627 )],
628 edges: vec![Edge::ValueRun(
629 0,
630 0,
631 &ValueRole::Data,
632 ObservableOutput::Flip,
633 &[0, 1],
634 )],
635 value_output: None,
636 boundary_outputs: Vec::new(),
637 };
638 postcard::from_bytes::<SubGraph>(&encode(&wire)).unwrap_err();
639 wire.edges.clear();
640 postcard::from_bytes::<SubGraph>(&encode(&wire)).unwrap();
641 wire.nodes.push(wire.nodes[0].clone());
642 postcard::from_bytes::<SubGraph>(&encode(&wire)).unwrap_err();
643 wire.nodes.pop();
644 wire.nodes[0].0 = u32::MAX;
645 postcard::from_bytes::<SubGraph>(&encode(&wire)).unwrap_err();
646 }
647
648 #[test]
649 fn binary_graph_accepts_unsorted_nodes_and_checks_every_edge_endpoint() {
650 let definitions = vec![ClassicalNode::Compute {
651 expr: ClassicalExpr::Const(false),
652 }];
653 let node = WireNode {
654 kind: WireKind::Classical(0),
655 provenance: NodeProvenance::None,
656 activation: None,
657 };
658 let nodes = vec![(2u32, node.clone()), (0u32, node)];
659 let wire = (
660 &definitions,
661 &nodes,
662 vec![Edge::ValueRun(
663 2,
664 0,
665 &ValueRole::Data,
666 ObservableOutput::Flip,
667 &[0, 0],
668 )],
669 Some(ValueRef::from(BloqNodeId(2))),
670 vec![BloqNodeId(0)],
671 );
672 let bytes = encode(&wire);
673 let mut restored = postcard::from_bytes::<SubGraph>(&bytes).unwrap();
674 assert!(restored.node(BloqNodeId(1)).is_none());
675 assert_eq!(restored.value_output(), Some(ValueRef::from(BloqNodeId(2))));
676 assert_eq!(restored.boundary_outputs(), &[BloqNodeId(0)]);
677 let mut reloaded = postcard::from_bytes::<SubGraph>(&encode(&restored)).unwrap();
678 for level in [&mut restored, &mut reloaded] {
679 level.add_edge(BloqNodeId(2), BloqNodeId(0), BloqEdge::Order);
680 }
681 assert_eq!(encode(&restored), encode(&reloaded));
682 for end in 0..bytes.len() {
683 postcard::from_bytes::<SubGraph>(&bytes[..end]).unwrap_err();
684 }
685
686 let quantum = QuantumEdge {
687 pipes: Vec::new(),
688 guard: None,
689 };
690 for edge in [
691 Edge::Quantum(1, 2, &quantum),
692 Edge::Value(0, 1, 0, &ValueRole::Data, ObservableOutput::Corrected),
693 Edge::Order(1, 2),
694 Edge::Compose(0, 1, 0, &ValueRole::Data),
695 Edge::ValueRun(2, 0, &ValueRole::Data, ObservableOutput::Flip, &[0, 1]),
696 ] {
697 let invalid = encode(&(
698 &definitions,
699 &nodes,
700 vec![edge],
701 None::<ValueRef>,
702 Vec::<BloqNodeId>::new(),
703 ));
704 postcard::from_bytes::<SubGraph>(&invalid).unwrap_err();
705 }
706 }
707
708 #[test]
709 fn graph_runs_keep_node_holes_declarations_and_human_readable_triples() {
710 let mut graph = SubGraph::new();
711 let nodes = (0..6)
712 .map(|_| {
713 graph.add_node(BloqNode::classical(ClassicalNode::Compute {
714 expr: ClassicalExpr::Const(false),
715 }))
716 })
717 .collect::<Vec<_>>();
718 graph.remove_node(nodes[1]).unwrap();
719 for (source, slot, role) in [
720 (0, 0, ValueRole::Data),
721 (0, 1, ValueRole::Data),
722 (2, 2, ValueRole::FeedbackFold { action: 9 }),
723 (3, 3, ValueRole::FeedbackFold { action: 9 }),
724 (2, 4, ValueRole::ReadoutFold),
725 (2, 5, ValueRole::ReadoutFold),
726 (3, u32::MAX, ValueRole::Data),
727 ] {
728 graph.add_edge(
729 nodes[source],
730 nodes[4],
731 BloqEdge::Value {
732 slot,
733 role,
734 output: if slot % 2 == 0 {
735 ObservableOutput::Flip
736 } else {
737 ObservableOutput::Corrected
738 },
739 },
740 );
741 if slot == 1 {
742 graph.add_edge(nodes[2], nodes[5], BloqEdge::Order);
743 }
744 }
745 graph.add_edge(nodes[0], nodes[4], BloqEdge::compose(100));
746 graph.node_mut(nodes[4]).unwrap().kind = crate::BloqNodeKind::Classical(
747 ClassicalNode::Compute {
748 expr: ClassicalExpr::parity([0, 1, 2, 3, 4, 5, u32::MAX], false),
749 }
750 .into(),
751 );
752 graph.set_value_output(Some(ValueRef::from(nodes[4])));
753 let json = serde_json::to_value(&graph).unwrap();
754 let triples = graph
755 .edges()
756 .map(|edge| (edge.source.0, edge.target.0, edge.edge))
757 .collect::<Vec<_>>();
758 assert_eq!(json["edges"], serde_json::to_value(triples).unwrap());
759 let binary = encode(&graph);
760 let mut unsorted_json = json.clone();
761 unsorted_json["nodes"].as_array_mut().unwrap().reverse();
762 for restored in [
763 postcard::from_bytes::<SubGraph>(&binary).unwrap(),
764 serde_json::from_value::<SubGraph>(json.clone()).unwrap(),
765 serde_json::from_value::<SubGraph>(unsorted_json).unwrap(),
766 ] {
767 assert!(restored.node(nodes[1]).is_none());
768 assert_eq!(restored.value_output(), Some(ValueRef::from(nodes[4])));
769 assert_eq!(serde_json::to_value(&restored).unwrap(), json);
770 assert_eq!(encode(&restored), binary);
771 }
772 }
773 }
774}
775
776impl serde::Serialize for SubGraph {
777 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
778 if !serializer.is_human_readable() {
779 return classical_data::serialize(self, serializer);
780 }
781 SubGraphWireRef {
782 nodes: self,
783 edges: self,
784 value_output: self.value_output(),
785 boundary_outputs: self.boundary_outputs(),
786 }
787 .serialize(serializer)
788 }
789}
790
791impl<'de> serde::Deserialize<'de> for SubGraph {
792 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
793 if !deserializer.is_human_readable() {
794 return deserializer.deserialize_struct(
795 "SubGraphWire",
796 &[
797 "classical",
798 "nodes",
799 "edges",
800 "value_output",
801 "boundary_outputs",
802 ],
803 BinaryGraph,
804 );
805 }
806 let wire = SubGraphWire::deserialize(deserializer)?;
807 let mut level = rebuild_level(wire.nodes, wire.edges).map_err(serde::de::Error::custom)?;
808 level.set_value_output(wire.value_output);
809 level.set_boundary_outputs(wire.boundary_outputs);
810 level.share_local_classical_data();
811 Ok(level)
812 }
813}
814
815struct BinaryGraph;
816
817impl<'de> serde::de::Visitor<'de> for BinaryGraph {
818 type Value = SubGraph;
819
820 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
821 formatter.write_str("graph nodes, edges and output declarations")
822 }
823
824 fn visit_seq<A: serde::de::SeqAccess<'de>>(
825 self,
826 mut sequence: A,
827 ) -> Result<SubGraph, A::Error> {
828 use serde::de::Error;
829
830 let nodes = classical_data::read_nodes(&mut sequence)?;
831 let mut level = rebuild_level(nodes, Vec::new()).map_err(A::Error::custom)?;
834 sequence
835 .next_element_seed(edge_runs::EdgeSequence(&mut level))?
836 .ok_or_else(|| A::Error::missing_field("edges"))?;
837 level.set_value_output(
838 sequence
839 .next_element()?
840 .ok_or_else(|| A::Error::missing_field("value_output"))?,
841 );
842 level.set_boundary_outputs(
843 sequence
844 .next_element()?
845 .ok_or_else(|| A::Error::missing_field("boundary_outputs"))?,
846 );
847 Ok(level)
848 }
849}
850
851#[derive(Debug, Clone, Copy)]
854pub struct BloqEdgeRef<'a> {
855 pub source: BloqNodeId,
857 pub target: BloqNodeId,
859 pub edge: &'a BloqEdge,
861}
862
863#[doc(hidden)]
867#[derive(Debug)]
868pub struct PathScratch(
869 petgraph::algo::DfsSpace<NodeIndex, <StableDiGraph<BloqNode, BloqEdge> as Visitable>::Map>,
870);
871
872fn to_index(id: BloqNodeId) -> NodeIndex {
873 NodeIndex::new(id.0 as usize)
874}
875
876fn to_id(index: NodeIndex) -> BloqNodeId {
877 BloqNodeId(index.index() as u32)
878}
879
880fn to_edge_ref(edge: petgraph::stable_graph::EdgeReference<'_, BloqEdge>) -> BloqEdgeRef<'_> {
881 BloqEdgeRef {
882 source: to_id(edge.source()),
883 target: to_id(edge.target()),
884 edge: edge.weight(),
885 }
886}
887
888#[derive(Debug, Clone, Copy)]
896pub struct ValueInput<'a> {
897 pub slot: u32,
899 pub producer: BloqNodeId,
901 pub role: &'a ValueRole,
903 pub output: Option<super::ObservableOutput>,
905}
906
907impl ValueInput<'_> {
908 pub fn value_ref(self) -> Option<super::ValueRef> {
910 self.output.map(|output| super::ValueRef {
911 node: self.producer,
912 output,
913 })
914 }
915}
916
917impl SubGraph {
918 pub fn new() -> Self {
920 Self::default()
921 }
922
923 pub fn value_output(&self) -> Option<super::ValueRef> {
926 self.data.value_output
927 }
928
929 pub fn set_value_output(&mut self, output: Option<super::ValueRef>) {
932 Arc::make_mut(&mut self.data).value_output = output;
933 }
934
935 pub fn boundary_outputs(&self) -> &[BloqNodeId] {
938 &self.data.boundary_outputs
939 }
940
941 pub fn set_boundary_outputs(&mut self, outputs: Vec<BloqNodeId>) {
944 Arc::make_mut(&mut self.data).boundary_outputs = outputs;
945 }
946
947 pub fn add_node(&mut self, node: BloqNode) -> BloqNodeId {
949 to_id(self.graph_mut().add_node(node))
950 }
951
952 pub fn add_edge(&mut self, from: BloqNodeId, to: BloqNodeId, edge: BloqEdge) {
959 self.graph_mut()
960 .add_edge(to_index(from), to_index(to), edge);
961 }
962
963 pub fn node(&self, id: BloqNodeId) -> Option<&BloqNode> {
965 self.graph().node_weight(to_index(id))
966 }
967
968 pub fn node_mut(&mut self, id: BloqNodeId) -> Option<&mut BloqNode> {
972 self.graph_mut().node_weight_mut(to_index(id))
973 }
974
975 pub fn remove_node(&mut self, id: BloqNodeId) -> Option<BloqNode> {
982 let data = Arc::make_mut(&mut self.data);
983 let node = data.graph.remove_node(to_index(id))?;
984 if data.value_output.is_some_and(|value| value.node == id) {
985 data.value_output = None;
986 }
987 data.boundary_outputs.retain(|&output| output != id);
988 Some(node)
989 }
990
991 pub fn node_ids(&self) -> impl DoubleEndedIterator<Item = BloqNodeId> + '_ {
993 self.graph().node_indices().map(to_id)
994 }
995
996 pub fn nodes(&self) -> impl DoubleEndedIterator<Item = (BloqNodeId, &BloqNode)> + '_ {
998 self.graph()
999 .node_indices()
1000 .map(|index| (to_id(index), &self.graph()[index]))
1001 }
1002
1003 pub fn quantum_nodes(&self) -> impl Iterator<Item = (BloqNodeId, &QuantumNode)> + '_ {
1006 self.nodes()
1007 .filter_map(|(id, node)| Some((id, node.try_quantum()?)))
1008 }
1009
1010 pub fn node_count(&self) -> usize {
1012 self.graph().node_count()
1013 }
1014
1015 pub fn edge_count(&self) -> usize {
1017 self.graph().edge_count()
1018 }
1019
1020 pub fn edges(&self) -> impl Iterator<Item = BloqEdgeRef<'_>> + '_ {
1022 self.graph().edge_references().map(to_edge_ref)
1023 }
1024
1025 pub fn for_each_edge_mut(&mut self, mut edit: impl FnMut(&mut BloqEdge)) {
1027 for edge in self.graph_mut().edge_weights_mut() {
1028 edit(edge);
1029 }
1030 }
1031
1032 pub fn incoming(&self, id: BloqNodeId) -> impl Iterator<Item = BloqEdgeRef<'_>> + '_ {
1034 self.graph()
1035 .edges_directed(to_index(id), Direction::Incoming)
1036 .map(to_edge_ref)
1037 }
1038
1039 pub fn outgoing(&self, id: BloqNodeId) -> impl Iterator<Item = BloqEdgeRef<'_>> + '_ {
1041 self.graph()
1042 .edges_directed(to_index(id), Direction::Outgoing)
1043 .map(to_edge_ref)
1044 }
1045
1046 pub fn edges_between(
1048 &self,
1049 from: BloqNodeId,
1050 to: BloqNodeId,
1051 ) -> impl Iterator<Item = BloqEdgeRef<'_>> + '_ {
1052 self.graph()
1053 .edges_connecting(to_index(from), to_index(to))
1054 .map(to_edge_ref)
1055 }
1056
1057 pub fn has_path(&self, from: BloqNodeId, to: BloqNodeId) -> bool {
1059 petgraph::algo::has_path_connecting(self.graph(), to_index(from), to_index(to), None)
1060 }
1061
1062 pub fn path_scratch(&self) -> PathScratch {
1064 PathScratch(petgraph::algo::DfsSpace::new(self.graph()))
1065 }
1066
1067 pub fn has_path_with_scratch(
1071 &self,
1072 from: BloqNodeId,
1073 to: BloqNodeId,
1074 scratch: &mut PathScratch,
1075 ) -> bool {
1076 petgraph::algo::has_path_connecting(
1077 self.graph(),
1078 to_index(from),
1079 to_index(to),
1080 Some(&mut scratch.0),
1081 )
1082 }
1083
1084 pub(crate) fn has_path_backwards_with_scratch(
1088 &self,
1089 from: BloqNodeId,
1090 to: BloqNodeId,
1091 scratch: &mut PathScratch,
1092 ) -> bool {
1093 petgraph::algo::has_path_connecting(
1094 petgraph::visit::Reversed(self.graph()),
1095 to_index(to),
1096 to_index(from),
1097 Some(&mut scratch.0),
1098 )
1099 }
1100
1101 pub fn value_inputs(&self, id: BloqNodeId) -> impl Iterator<Item = ValueInput<'_>> + '_ {
1105 self.incoming(id).filter_map(|edge| match edge.edge {
1106 BloqEdge::Value { slot, role, output } => Some(ValueInput {
1107 slot: *slot,
1108 producer: edge.source,
1109 role,
1110 output: Some(*output),
1111 }),
1112 _ => None,
1113 })
1114 }
1115
1116 pub fn data_inputs(&self, id: BloqNodeId) -> impl Iterator<Item = ValueInput<'_>> + '_ {
1118 self.incoming(id).filter_map(move |edge| {
1119 let (slot, role, output) = match edge.edge {
1120 BloqEdge::Value { slot, role, output } => (*slot, role, Some(*output)),
1121 BloqEdge::Compose { slot, role } => (*slot, role, None),
1122 _ => return None,
1123 };
1124 (Some(slot) != self[id].activation).then_some(ValueInput {
1125 slot,
1126 producer: edge.source,
1127 role,
1128 output,
1129 })
1130 })
1131 }
1132
1133 pub fn value_consumers(&self, id: BloqNodeId) -> impl Iterator<Item = (BloqNodeId, u32)> + '_ {
1135 self.outgoing(id).filter_map(|edge| match edge.edge {
1136 BloqEdge::Value { slot, .. } => Some((edge.target, *slot)),
1137 _ => None,
1138 })
1139 }
1140
1141 pub fn data_consumers(&self, id: BloqNodeId) -> impl Iterator<Item = (BloqNodeId, u32)> + '_ {
1143 self.outgoing(id).filter_map(|edge| match edge.edge {
1144 BloqEdge::Value { slot, .. } | BloqEdge::Compose { slot, .. } => {
1145 Some((edge.target, *slot))
1146 }
1147 _ => None,
1148 })
1149 }
1150
1151 pub fn compose_consumers(
1153 &self,
1154 id: BloqNodeId,
1155 ) -> impl Iterator<Item = (BloqNodeId, u32)> + '_ {
1156 self.outgoing(id).filter_map(|edge| match edge.edge {
1157 BloqEdge::Compose { slot, .. } => Some((edge.target, *slot)),
1158 _ => None,
1159 })
1160 }
1161
1162 pub fn open_value_producers(&self) -> impl Iterator<Item = BloqNodeId> + '_ {
1166 self.nodes()
1167 .filter(|(id, node)| {
1168 (node.try_region().is_some()
1169 || matches!(
1170 node.try_classical(),
1171 Some(ClassicalNode::Compute { .. } | ClassicalNode::Observable { .. })
1172 ))
1173 && self.value_consumers(*id).next().is_none()
1174 })
1175 .map(|(id, _)| id)
1176 }
1177
1178 pub fn open_bit_producers(&self) -> impl Iterator<Item = BloqNodeId> + '_ {
1181 self.open_value_producers()
1182 }
1183
1184 pub fn deterministic_emit_order(&self) -> Result<Vec<BloqNodeId>, crate::CycleDetected> {
1192 super::program::deterministic_emit_order_of(self.graph())
1193 }
1194
1195 pub fn is_acyclic(&self) -> bool {
1198 super::program::is_acyclic_of(self.graph(), &[])
1199 }
1200
1201 pub fn is_acyclic_with_edges(&self, extra: &[(BloqNodeId, BloqNodeId)]) -> bool {
1206 super::program::is_acyclic_of(self.graph(), extra)
1207 }
1208
1209 pub(crate) fn graph(&self) -> &StableDiGraph<BloqNode, BloqEdge> {
1210 &self.data.graph
1211 }
1212
1213 pub(crate) fn graph_mut(&mut self) -> &mut StableDiGraph<BloqNode, BloqEdge> {
1214 &mut Arc::make_mut(&mut self.data).graph
1215 }
1216}
1217
1218impl Index<BloqNodeId> for SubGraph {
1221 type Output = BloqNode;
1222
1223 fn index(&self, id: BloqNodeId) -> &Self::Output {
1224 self.node(id)
1225 .expect("BloqNodeId is live in this graph level (spec rule SEM-ID)")
1226 }
1227}