1use vstd::prelude::*;
10
11use crate::primitives::resource_registry::ResourceRegistry;
12
13verus! {
14
15pub type EdgeKey = (usize, usize, u64);
17pub type EdgeBinding = (EdgeKey, ());
19
20pub open spec fn edge_admitted(
22 num_nodes: usize,
23 max_weight: u64,
24 source: usize,
25 target: usize,
26 weight: u64,
27) -> bool {
28 source < num_nodes && target < num_nodes && weight <= max_weight
29}
30
31pub open spec fn adjacency_admitted(
33 num_nodes: usize,
34 source: usize,
35 target: usize,
36) -> bool {
37 source < num_nodes && target < num_nodes
38}
39
40pub open spec fn adjacency_consistent(
42 adjacency_present: bool,
43 edge_present: bool,
44) -> bool {
45 crate::connectives::projection::membership_consistent(
46 adjacency_present,
47 edge_present,
48 )
49}
50
51pub open spec fn edge_irreflexive(present: bool, source: usize, target: usize) -> bool {
53 present ==> source != target
54}
55
56pub open spec fn has_edge(
58 entries: Seq<EdgeBinding>,
59 n: int,
60 source: usize,
61 target: usize,
62) -> bool {
63 exists|index: int|
64 0 <= index < n
65 && entries[index].0.0 == source
66 && entries[index].0.1 == target
67}
68
69pub open spec fn has_exact_edge(
71 entries: Seq<EdgeBinding>,
72 n: int,
73 source: usize,
74 target: usize,
75 weight: u64,
76) -> bool {
77 crate::primitives::resource_registry::has_pair(
78 entries,
79 n,
80 (source, target, weight),
81 (),
82 )
83}
84
85pub proof fn lemma_has_edge_extend(
87 entries: Seq<EdgeBinding>,
88 n: int,
89 source: usize,
90 target: usize,
91)
92 requires 0 <= n < entries.len(),
93 ensures
94 has_edge(entries, n + 1, source, target)
95 == (has_edge(entries, n, source, target)
96 || (entries[n].0.0 == source && entries[n].0.1 == target)),
97{
98 if has_edge(entries, n + 1, source, target) {
99 let index = choose|index: int|
100 0 <= index < n + 1
101 && entries[index].0.0 == source
102 && entries[index].0.1 == target;
103 assert(index < n || index == n);
104 }
105 if has_edge(entries, n, source, target) {
106 let index = choose|index: int|
107 0 <= index < n
108 && entries[index].0.0 == source
109 && entries[index].0.1 == target;
110 assert(0 <= index < n + 1);
111 }
112 if entries[n].0.0 == source && entries[n].0.1 == target {
113 assert(0 <= n < n + 1);
114 }
115}
116
117pub proof fn lemma_push_has_edge(
119 entries: Seq<(EdgeKey, ())>,
120 added: EdgeKey,
121 source: usize,
122 target: usize,
123)
124 ensures has_edge(entries.push((added, ())), entries.len() as int + 1, source, target)
125 == (has_edge(entries, entries.len() as int, source, target)
126 || (added.0 == source && added.1 == target)),
127{
128 let pushed = entries.push((added, ()));
129 if has_edge(pushed, pushed.len() as int, source, target) {
130 let index = choose|index: int|
131 0 <= index < pushed.len()
132 && pushed[index].0.0 == source
133 && pushed[index].0.1 == target;
134 if index < entries.len() {
135 assert(pushed[index] == entries[index]);
136 } else {
137 assert(index == entries.len());
138 }
139 }
140 if has_edge(entries, entries.len() as int, source, target) {
141 let index = choose|index: int|
142 0 <= index < entries.len()
143 && entries[index].0.0 == source
144 && entries[index].0.1 == target;
145 assert(pushed[index] == entries[index]);
146 }
147 if added.0 == source && added.1 == target {
148 assert(pushed[entries.len() as int].0 == added);
149 }
150}
151
152pub struct RelationshipGraph {
154 pub num_nodes: usize,
156 pub max_weight: u64,
158 pub registry: ResourceRegistry<EdgeKey, ()>,
160}
161
162impl RelationshipGraph {
163 pub open spec fn edge_proj(&self, source: usize, target: usize) -> bool {
165 has_edge(
166 self.registry.entries@,
167 self.registry.entries@.len() as int,
168 source,
169 target,
170 )
171 }
172
173 pub open spec fn exact_edge(&self, source: usize, target: usize, weight: u64) -> bool {
175 self.registry.maps_to((source, target, weight), ())
176 }
177
178 pub proof fn exact_edge_implies_pair(&self, source: usize, target: usize, weight: u64)
180 ensures self.exact_edge(source, target, weight) ==> self.edge_proj(source, target),
181 {
182 if self.exact_edge(source, target, weight) {
183 let index = choose|index: int|
184 0 <= index < self.registry.entries@.len()
185 && self.registry.entries@[index].0 == (source, target, weight)
186 && self.registry.entries@[index].1 == ();
187 assert(self.registry.entries@[index].0.0 == source);
188 assert(self.registry.entries@[index].0.1 == target);
189 }
190 }
191
192 pub open spec fn adj_proj(&self, source: usize, target: usize) -> bool {
194 self.edge_proj(source, target)
195 }
196
197 pub open spec fn type_invariant(&self) -> bool {
199 &&& self.registry.unique_mapping()
200 &&& forall|index: int|
201 #![trigger self.registry.entries@[index]]
202 0 <= index < self.registry.entries@.len() ==> edge_admitted(
203 self.num_nodes,
204 self.max_weight,
205 self.registry.entries@[index].0.0,
206 self.registry.entries@[index].0.1,
207 self.registry.entries@[index].0.2,
208 )
209 }
210
211 pub open spec fn adjacency_consistency(&self) -> bool {
213 forall|source: usize, target: usize|
214 source < self.num_nodes && target < self.num_nodes ==> adjacency_consistent(
215 #[trigger] self.adj_proj(source, target),
216 self.edge_proj(source, target),
217 )
218 }
219
220 pub open spec fn no_self_loops(&self) -> bool {
222 forall|index: int|
223 #![trigger self.registry.entries@[index]]
224 0 <= index < self.registry.entries@.len() ==> edge_irreflexive(
225 true,
226 self.registry.entries@[index].0.0,
227 self.registry.entries@[index].0.1,
228 )
229 }
230
231 pub open spec fn inv(&self) -> bool {
233 self.type_invariant() && self.adjacency_consistency() && self.no_self_loops()
234 }
235
236 pub proof fn expose_storage_facts(&self)
238 requires self.inv(),
239 ensures
240 self.registry.unique_mapping(),
241 forall|index: int| #![trigger self.registry.entries@[index]]
242 0 <= index < self.registry.entries@.len() ==>
243 self.registry.entries@[index].0.0 < self.num_nodes
244 && self.registry.entries@[index].0.1 < self.num_nodes
245 && self.registry.entries@[index].0.2 <= self.max_weight
246 && self.registry.entries@[index].0.0
247 != self.registry.entries@[index].0.1,
248 {
249 reveal(RelationshipGraph::inv);
250 reveal(RelationshipGraph::type_invariant);
251 reveal(RelationshipGraph::no_self_loops);
252 reveal(edge_admitted);
253 reveal(edge_irreflexive);
254 }
255
256 pub fn new(num_nodes: usize, max_weight: u64) -> (graph: RelationshipGraph)
258 ensures
259 graph.num_nodes == num_nodes,
260 graph.max_weight == max_weight,
261 graph.registry.entries@.len() == 0,
262 graph.inv(),
263 {
264 let registry = ResourceRegistry::new();
265 RelationshipGraph { num_nodes, max_weight, registry }
266 }
267
268 pub fn can_add_edge(&self, source: usize, target: usize, weight: u64) -> (enabled: bool)
270 ensures enabled == (source < self.num_nodes
271 && target < self.num_nodes
272 && weight <= self.max_weight
273 && source != target),
274 {
275 source < self.num_nodes
276 && target < self.num_nodes
277 && weight <= self.max_weight
278 && source != target
279 }
280
281 pub fn contains_exact_edge(
283 &self,
284 source: usize,
285 target: usize,
286 weight: u64,
287 ) -> (present: bool)
288 requires self.registry.unique_mapping(),
289 ensures present == self.exact_edge(source, target, weight),
290 {
291 match self.registry.lookup((source, target, weight)) {
292 Some(_) => true,
293 None => false,
294 }
295 }
296
297 pub fn contains_pair(&self, source: usize, target: usize) -> (present: bool)
299 ensures present == self.edge_proj(source, target),
300 {
301 let length = self.registry.entries.len();
302 let mut index: usize = 0;
303 while index < length
304 invariant
305 index <= length,
306 length == self.registry.entries.len(),
307 !has_edge(self.registry.entries@, index as int, source, target),
308 decreases length - index,
309 {
310 let key = self.registry.entries[index].0;
311 if key.0 == source && key.1 == target {
312 return true;
313 }
314 proof {
315 lemma_has_edge_extend(
316 self.registry.entries@,
317 index as int,
318 source,
319 target,
320 );
321 }
322 index = index + 1;
323 }
324 false
325 }
326
327 pub fn add_edge(
329 &mut self,
330 source: usize,
331 target: usize,
332 weight: u64,
333 ) -> (added: bool)
334 requires
335 old(self).inv(),
336 source < old(self).num_nodes,
337 target < old(self).num_nodes,
338 weight <= old(self).max_weight,
339 source != target,
340 ensures
341 final(self).inv(),
342 final(self).num_nodes == old(self).num_nodes,
343 final(self).max_weight == old(self).max_weight,
344 added == !old(self).exact_edge(source, target, weight),
345 !added ==> final(self).registry.entries@ == old(self).registry.entries@,
346 added ==> final(self).registry.entries@
347 == old(self).registry.entries@.push(((source, target, weight), ())),
348 forall|other_source: usize, other_target: usize|
349 #[trigger] final(self).edge_proj(other_source, other_target)
350 == (old(self).edge_proj(other_source, other_target)
351 || (other_source == source && other_target == target)),
352 {
353 proof { self.expose_storage_facts(); }
354 if self.contains_exact_edge(source, target, weight) {
355 return false;
356 }
357 let ghost before = self.registry.entries@;
358 self.registry.register((source, target, weight), ());
359 assert(self.registry.entries@ == before.push(((source, target, weight), ())));
360 assert forall|other_source: usize, other_target: usize|
361 #[trigger] self.edge_proj(other_source, other_target)
362 == (has_edge(before, before.len() as int, other_source, other_target)
363 || (other_source == source && other_target == target)) by {
364 lemma_push_has_edge(
365 before,
366 (source, target, weight),
367 other_source,
368 other_target,
369 );
370 }
371 assert(self.type_invariant()) by {
372 assert forall|index: int| #![trigger self.registry.entries@[index]]
373 0 <= index < self.registry.entries@.len() implies edge_admitted(
374 self.num_nodes,
375 self.max_weight,
376 self.registry.entries@[index].0.0,
377 self.registry.entries@[index].0.1,
378 self.registry.entries@[index].0.2,
379 ) by {
380 if index < before.len() {
381 assert(self.registry.entries@[index] == before[index]);
382 } else {
383 assert(index == before.len());
384 assert(self.registry.entries@[index] == ((source, target, weight), ()));
385 }
386 }
387 }
388 assert(self.no_self_loops()) by {
389 assert forall|index: int| #![trigger self.registry.entries@[index]]
390 0 <= index < self.registry.entries@.len() implies edge_irreflexive(
391 true,
392 self.registry.entries@[index].0.0,
393 self.registry.entries@[index].0.1,
394 ) by {
395 if index < before.len() {
396 assert(self.registry.entries@[index] == before[index]);
397 } else {
398 assert(index == before.len());
399 assert(self.registry.entries@[index] == ((source, target, weight), ()));
400 }
401 }
402 }
403 assert(self.adjacency_consistency()) by {
404 reveal(RelationshipGraph::adjacency_consistency);
405 reveal(RelationshipGraph::adj_proj);
406 reveal(adjacency_consistent);
407 reveal(crate::connectives::projection::membership_consistent);
408 }
409 true
410 }
411
412 pub fn remove_edge(&mut self, source: usize, target: usize)
414 requires old(self).inv(),
415 ensures
416 final(self).inv(),
417 final(self).num_nodes == old(self).num_nodes,
418 final(self).max_weight == old(self).max_weight,
419 forall|s: usize, d: usize, weight: u64|
420 #[trigger] final(self).exact_edge(s, d, weight)
421 == (!(s == source && d == target)
422 && old(self).exact_edge(s, d, weight)),
423 !final(self).edge_proj(source, target),
424 {
425 proof { self.expose_storage_facts(); }
426 let ghost original = self.registry.entries@;
427 let ghost original_num_nodes = self.num_nodes;
428 let ghost original_max_weight = self.max_weight;
429 let mut index: usize = 0;
430 while index < self.registry.entries.len()
431 invariant
432 index <= self.registry.entries.len(),
433 self.num_nodes == original_num_nodes,
434 self.max_weight == original_max_weight,
435 self.registry.unique_mapping(),
436 forall|entry: int| #![trigger self.registry.entries@[entry]]
437 0 <= entry < self.registry.entries@.len() ==>
438 self.registry.entries@[entry].0.0 < self.num_nodes
439 && self.registry.entries@[entry].0.1 < self.num_nodes
440 && self.registry.entries@[entry].0.2 <= self.max_weight
441 && self.registry.entries@[entry].0.0
442 != self.registry.entries@[entry].0.1,
443 forall|entry: int| #![trigger self.registry.entries@[entry]]
444 0 <= entry < index ==>
445 !(self.registry.entries@[entry].0.0 == source
446 && self.registry.entries@[entry].0.1 == target),
447 forall|s: usize, d: usize, weight: u64|
448 !(s == source && d == target) ==>
449 (#[trigger] has_exact_edge(
450 self.registry.entries@,
451 self.registry.entries@.len() as int,
452 s,
453 d,
454 weight,
455 ) == has_exact_edge(
456 original,
457 original.len() as int,
458 s,
459 d,
460 weight,
461 )),
462 decreases self.registry.entries.len() - index,
463 {
464 let key = self.registry.entries[index].0;
465 if key.0 == source && key.1 == target {
466 let ghost before = self.registry.entries@;
467 let _removed = self.registry.deregister_at(index);
468 assert(_removed.0 == key);
469 assert forall|entry: int| #![trigger self.registry.entries@[entry]]
470 0 <= entry < self.registry.entries@.len() implies
471 self.registry.entries@[entry].0.0 < self.num_nodes
472 && self.registry.entries@[entry].0.1 < self.num_nodes
473 && self.registry.entries@[entry].0.2 <= self.max_weight
474 && self.registry.entries@[entry].0.0
475 != self.registry.entries@[entry].0.1 by {
476 before.remove_ensures(index as int);
477 let old_entry = if entry < index { entry } else { entry + 1 };
478 assert(0 <= old_entry < before.len());
479 assert(self.registry.entries@[entry] == before[old_entry]);
480 }
481 assert forall|entry: int| #![trigger self.registry.entries@[entry]]
482 0 <= entry < index implies
483 !(self.registry.entries@[entry].0.0 == source
484 && self.registry.entries@[entry].0.1 == target) by {
485 before.remove_ensures(index as int);
486 assert(self.registry.entries@[entry] == before[entry]);
487 }
488 assert forall|s: usize, d: usize, weight: u64|
489 !(s == source && d == target) implies
490 (#[trigger] has_exact_edge(
491 self.registry.entries@,
492 self.registry.entries@.len() as int,
493 s,
494 d,
495 weight,
496 ) == has_exact_edge(
497 original,
498 original.len() as int,
499 s,
500 d,
501 weight,
502 )) by {
503 assert((s, d, weight) != key);
504 assert(self.registry.maps_to((s, d, weight), ()) ==
505 (has_exact_edge(
506 before,
507 before.len() as int,
508 s,
509 d,
510 weight,
511 ) && (s, d, weight) != key));
512 assert(has_exact_edge(
513 before,
514 before.len() as int,
515 s,
516 d,
517 weight,
518 ) == has_exact_edge(
519 original,
520 original.len() as int,
521 s,
522 d,
523 weight,
524 ));
525 }
526 } else {
527 index = index + 1;
528 }
529 }
530 assert(!self.edge_proj(source, target)) by {
531 if self.edge_proj(source, target) {
532 let entry = choose|entry: int|
533 0 <= entry < self.registry.entries@.len()
534 && self.registry.entries@[entry].0.0 == source
535 && self.registry.entries@[entry].0.1 == target;
536 assert(false);
537 }
538 }
539 assert(self.type_invariant());
540 assert(self.no_self_loops());
541 assert(self.adjacency_consistency()) by {
542 reveal(RelationshipGraph::adjacency_consistency);
543 reveal(RelationshipGraph::adj_proj);
544 reveal(adjacency_consistent);
545 reveal(crate::connectives::projection::membership_consistent);
546 }
547 assert forall|s: usize, d: usize, weight: u64|
548 #[trigger] self.exact_edge(s, d, weight)
549 == (!(s == source && d == target)
550 && old(self).exact_edge(s, d, weight)) by {
551 if s == source && d == target {
552 if self.exact_edge(s, d, weight) {
553 let entry = choose|entry: int|
554 0 <= entry < self.registry.entries@.len()
555 && self.registry.entries@[entry].0 == (s, d, weight)
556 && self.registry.entries@[entry].1 == ();
557 assert(has_edge(
558 self.registry.entries@,
559 self.registry.entries@.len() as int,
560 source,
561 target,
562 ));
563 assert(self.edge_proj(source, target));
564 }
565 } else {
566 assert(self.exact_edge(s, d, weight) == has_exact_edge(
567 self.registry.entries@,
568 self.registry.entries@.len() as int,
569 s,
570 d,
571 weight,
572 ));
573 assert(old(self).exact_edge(s, d, weight) == has_exact_edge(
574 original,
575 original.len() as int,
576 s,
577 d,
578 weight,
579 ));
580 }
581 }
582 }
583}
584
585}