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