Skip to main content

vstd_extra/
function_properties.rs

1//! Properties of pure mathematical spec functions ([`spec_fn`](https://verus-lang.github.io/verus/guide/spec_functions.html)).
2use vstd::prelude::*;
3use vstd::relations::*;
4use vstd::set_lib::*;
5
6verus! {
7
8/// A function is bijective from `domain` to `codomain`
9/// if it is injective on `domain` and its image equals `codomain`.
10pub open spec fn bijective_on<A, B>(f: spec_fn(A) -> B, domain: Set<A>, codomain: Set<B>) -> bool {
11    domain.injective_on(f) && domain.map(f) =~= codomain
12}
13
14/// `g` is a left inverse of `f` on `domain` if `g(f(x)) == x` for all `x` in `domain`,
15/// and `f(x)` lies in `codomain`.
16pub open spec fn left_inverse_on<A, B>(
17    f: spec_fn(A) -> B,
18    g: spec_fn(B) -> A,
19    domain: Set<A>,
20    codomain: Set<B>,
21) -> bool {
22    domain.all(|x: A| codomain.contains(f(x)) && g(f(x)) == x)
23}
24
25/// `g` is a right inverse of `f` on `codomain` if `f(g(y)) == y` for all `y` in `codomain`,
26/// and `g(y)` lies in `domain`.
27pub open spec fn right_inverse_on<A, B>(
28    f: spec_fn(A) -> B,
29    g: spec_fn(B) -> A,
30    domain: Set<A>,
31    codomain: Set<B>,
32) -> bool {
33    codomain.all(|y: B| domain.contains(g(y)) && f(g(y)) == y)
34}
35
36/// `g` is a two-sided inverse of `f` if it is both a left and right inverse.
37pub open spec fn inverse_on<A, B>(
38    f: spec_fn(A) -> B,
39    g: spec_fn(B) -> A,
40    domain: Set<A>,
41    codomain: Set<B>,
42) -> bool {
43    left_inverse_on(f, g, domain, codomain) && right_inverse_on(f, g, domain, codomain)
44}
45
46/// Constructs a left inverse function `g` of `f` when `f` is injective on `domain`.
47/// For each `b` in the image, returns the unique `a` such that `f(a) = b`.
48pub open spec fn construct_left_inverse<A, B>(f: spec_fn(A) -> B, domain: Set<A>) -> spec_fn(B) -> A
49    recommends
50        domain.injective_on(f),
51{
52    |b: B| choose|a: A| domain.contains(a) && f(a) == b
53}
54
55/// Constructs the inverse of a bijective function `f` from `domain` to `codomain`.
56/// For each `b` in `codomain`, returns the unique `a` in `domain` such that `f(a) == b`.
57pub open spec fn construct_inverse<A, B>(
58    f: spec_fn(A) -> B,
59    domain: Set<A>,
60    codomain: Set<B>,
61) -> spec_fn(B) -> A
62    recommends
63        bijective_on(f, domain, codomain),
64{
65    |b: B| choose|a: A| domain.contains(a) && f(a) == b
66}
67
68/// If `f` is injective on `domain`, then `construct_left_inverse(f, domain)`
69/// is a left inverse of `f` on that domain.
70/// That is, for all `x ∈ domain`, we have `g(f(x)) == x`.
71pub proof fn lemma_construct_left_inverse_sound<A, B>(f: spec_fn(A) -> B, domain: Set<A>)
72    requires
73        domain.injective_on(f),
74    ensures
75        left_inverse_on(f, construct_left_inverse(f, domain), domain, domain.map(f)),
76{
77}
78
79/// If `f` is bijective from `domain` to `codomain`, then `construct_inverse(f, domain, codomain)`
80/// is a two-sided inverse of `f` on that domain and codomain.
81/// That is, for all `x ∈ domain`, we have `g(f(x)) == x` and for all `y ∈ codomain`, we have `f(g(y)) == y`.
82pub proof fn lemma_construct_inverse_sound<A, B>(
83    f: spec_fn(A) -> B,
84    domain: Set<A>,
85    codomain: Set<B>,
86)
87    requires
88        bijective_on(f, domain, codomain),
89    ensures
90        inverse_on(f, construct_inverse(f, domain, codomain), domain, codomain),
91{
92}
93
94/// A function is injective on the whole type implies that it is injective on any sub-domain.
95pub proof fn lemma_injective_implies_injective_on<T, U>(f: spec_fn(T) -> U, dom: Set<T>)
96    requires
97        injective(f),
98    ensures
99        dom.injective_on(f),
100{
101}
102
103/// If `f` has a two-sided inverse `g` on `domain` and `codomain`, then `f` is bijective on that domain and codomain.
104pub proof fn lemma_two_sided_inverse_implies_bijective<A, B>(
105    f: spec_fn(A) -> B,
106    g: spec_fn(B) -> A,
107    domain: Set<A>,
108    codomain: Set<B>,
109)
110    requires
111        inverse_on(f, g, domain, codomain),
112    ensures
113        bijective_on(f, domain, codomain),
114{
115}
116
117/// If `f` is a bijection from `domain` to `codomain`, and `g` is its left inverse,
118/// then `g` is a bijection from `codomain` to `domain`.
119pub proof fn lemma_left_inverse_of_bijection_is_bijective<A, B>(
120    f: spec_fn(A) -> B,
121    g: spec_fn(B) -> A,
122    domain: Set<A>,
123    codomain: Set<B>,
124)
125    requires
126        bijective_on(f, domain, codomain),
127        left_inverse_on(f, g, domain, codomain),
128    ensures
129        bijective_on(g, codomain, domain),
130{
131}
132
133/// If `f` is a bijection from `domain` to `codomain`, and `g` is its right inverse,
134/// then `g` is a bijection from `codomain` to `domain`.
135pub proof fn lemma_right_inverse_of_bijection_is_bijective<A, B>(
136    f: spec_fn(A) -> B,
137    g: spec_fn(B) -> A,
138    domain: Set<A>,
139    codomain: Set<B>,
140)
141    requires
142        bijective_on(f, domain, codomain),
143        right_inverse_on(f, g, domain, codomain),
144    ensures
145        bijective_on(g, codomain, domain),
146{
147    assert forall|x| #[trigger] codomain.map(g).contains(x) == domain.contains(x) by {
148        if domain.contains(x) {
149            assert(codomain.contains(f(x)));
150        }
151    }
152}
153
154/// If `f` is a bijection from `domain` to `codomain`, and `g` is either its left or right inverse,
155/// then `g` is a bijection from `codomain` to `domain`.
156pub proof fn lemma_inverse_of_bijection_is_bijective<A, B>(
157    f: spec_fn(A) -> B,
158    g: spec_fn(B) -> A,
159    domain: Set<A>,
160    codomain: Set<B>,
161)
162    requires
163        bijective_on(f, domain, codomain),
164        left_inverse_on(f, g, domain, codomain) || right_inverse_on(f, g, domain, codomain),
165    ensures
166        bijective_on(g, codomain, domain),
167{
168    if left_inverse_on(f, g, domain, codomain) {
169        lemma_left_inverse_of_bijection_is_bijective(f, g, domain, codomain);
170    } else {
171        lemma_right_inverse_of_bijection_is_bijective(f, g, domain, codomain);
172    }
173}
174
175/// Mapping a finite set with an injective function results in a set of the same cardinality.
176pub proof fn lemma_injective_map_cardinality<T, U>(f: spec_fn(T) -> U, dom: Set<T>, s: Set<T>)
177    requires
178        dom.injective_on(f),
179        s <= dom,
180    ensures
181        s.len() == s.map(f).len(),
182    decreases s.len(),
183{
184    if s.is_empty() {
185        assert(s.map(f) == Set::empty());
186    } else {
187        let x = s.choose();
188        lemma_injective_map_cardinality(f, dom, s.remove(x));
189        assert(s.map(f) == s.remove(x).map(f).insert(f(x)));
190    }
191}
192
193pub proof fn lemma_bijective_cardinality<A, B>(f: spec_fn(A) -> B, domain: Set<A>, codomain: Set<B>)
194    requires
195        bijective_on(f, domain, codomain),
196    ensures
197        domain.len() == codomain.len(),
198{
199    lemma_injective_map_cardinality(f, domain, domain);
200}
201
202pub proof fn lemma_bijective_subset_still_bijective<A, B>(
203    f: spec_fn(A) -> B,
204    domain: Set<A>,
205    codomain: Set<B>,
206    s: Set<A>,
207)
208    requires
209        bijective_on(f, domain, codomain),
210        s <= domain,
211    ensures
212        bijective_on(f, s, s.map(f)),
213{
214}
215
216} // verus!