Skip to content

Commit db1b95b

Browse files
authored
Merge pull request #98 from VariantSync/merge-thesis_bm-to-main
Formalization of Succinctness
2 parents 5a05d91 + f536bb1 commit db1b95b

58 files changed

Lines changed: 5636 additions & 114 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,14 @@ Details on the features implemented in Vatras, including tutorials for integrati
130130
131131
The PhD thesis presents a refined and extended version of our OOPSLA'24 paper in chapter 3. In the thesis, we extended the discussion on existing variability languages to better reflect and compare their assumptions on the underlying object language and their varying semantic domains. Consequently, we also extended our formal framework and case study to also study differences in semantic domains. We also refined the notation to better align with the Agda code and to avoid some minor ambiguities.
132132

133+
### On the Succinctness of Languages for Static Variability
134+
135+
[![Thesis](https://img.shields.io/badge/Thesis-PDF-purple)](https://doi.org/10.18725/OPARU-59127)
136+
137+
> Benjamin Moosherr. _On the Succinctness of Languages for Static Variability_. Master Thesis, University of Ulm, November 2025. Reviewed by Matthias Tichy and Thomas Thüm. Supervised by Paul Maximilian Bittner.
138+
139+
This master thesis extends Vatras by a succinctness relation. Succinctness expresses how the size of expressions must change when translated using a variability language compiler. Hence, succinctness allows us to differentiate between variability languages which have equal semantic expressiveness but whose translation results in combinatorial explosion.
140+
133141
### On the Expressive Power of Languages for Static Variability (OOPSLA'24)
134142

135143
[![Preprint](https://img.shields.io/badge/OOPSLA'24-Preprint-purple)](https://github.com/SoftVarE-Group/Papers/raw/main/2024/2024-OOPSLA-Bittner.pdf)

src/Vatras/Data/IndexedSet.lagda.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -882,3 +882,15 @@ re-index {_≈ᵃ_ = _≈ᵃ_} rename M rename-is-surjective ≈ᵃ-refl ≈ᵇ-
882882
, re-indexʳ {_≈ᵃ_ = _≈ᵃ_} rename M rename-is-surjective ≈ᵃ-refl ≈ᵇ-sym M-is-congruent
883883
```
884884

885+
### Ungrouped Properties
886+
887+
```agda
888+
module _ where
889+
open import Data.Nat as ℕ using (ℕ)
890+
open import Data.Fin as Fin using (Fin)
891+
open import Data.List using (lookup; tabulate)
892+
893+
tabulate⁺ : ∀ {j} {J : Set j} {n : ℕ} {A : IndexedSet (Fin n)} {B : IndexedSet J} → A ⊆ B → lookup (tabulate A) ⊆ B
894+
tabulate⁺ {n = ℕ.suc n} x Fin.zero = x Fin.zero
895+
tabulate⁺ {n = ℕ.suc n} x (Fin.suc i) = tabulate⁺ (x ∘ Fin.suc) i
896+
```

src/Vatras/Framework/Definitions.agda

Lines changed: 46 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
module Vatras.Framework.Definitions where
22

33
open import Data.Maybe using (Maybe; just)
4+
open import Data.Nat as ℕ using (ℕ; zero)
45
open import Data.Product using (_×_; Σ; Σ-syntax; proj₁; proj₂) renaming (_,_ to _and_)
6+
import Data.Product.Properties as Product
7+
open import Data.String as String using (String)
58
open import Data.Unit using (⊤; tt) public
6-
open import Function using (id; _∘_)
9+
open import Function using (id; _∘_; const)
710
open import Relation.Binary.PropositionalEquality as Eq using (_≡_; _≗_; refl)
811
open import Relation.Binary using (DecidableEquality)
912
open import Relation.Nullary.Negation using (¬_)
@@ -20,12 +23,14 @@ the core definitions because it is quite reasonable.
2023
Any actual data we can think of to plug in here (e.g., strings, tokens or
2124
nodes of an abstract syntax tree) can be checked for equality.
2225
-}
23-
𝔸 : Set₁
24-
𝔸 = Σ Set DecidableEquality
25-
26-
-- retrieve the set of atoms from an atom type 𝔸
27-
atoms : 𝔸 Set
28-
atoms = proj₁
26+
record 𝔸 : Set₁ where
27+
-- We do not actually need eta equality in Vatras and it does break a proof when enabled (no idea why).
28+
no-eta-equality
29+
field
30+
atoms : Set
31+
atomsEqual? : DecidableEquality atoms
32+
atomSize : atoms
33+
open 𝔸 public
2934

3035
{-|
3136
Variant Language.
@@ -63,14 +68,39 @@ and hence expressions are parameterized in the type of this atomic data.
6368
𝔼 = 𝔸 Set₁
6469

6570
-- some default atoms
66-
module _ where
67-
open import Data.String using (String; _≟_)
68-
69-
STRING : 𝔸
70-
STRING = String and _≟_
71+
{-|
72+
String artifacts.
73+
Equality is defined character wise
74+
and size is measured by length (in characters not bytes).
75+
-}
76+
STRING : 𝔸
77+
STRING = record
78+
{ atoms = String
79+
; atomsEqual? = String._≟_
80+
; atomSize = String.length
81+
}
7182

72-
module _ where
73-
open import Data.Nat using (ℕ; _≟_)
83+
{-|
84+
Pairs of natural numbers as artifacts.
85+
The first element in the pair is treated as an identifier
86+
whereas the second element determines the size of the artifact.
87+
Both elements of the pair are tested for equality.
88+
-}
89+
NAT : 𝔸
90+
NAT = record
91+
{ atoms = ℕ × ℕ
92+
; atomsEqual? = Product.≡-dec ℕ._≟_ ℕ._≟_
93+
; atomSize = proj₂
94+
}
7495

75-
NAT : 𝔸
76-
NAT = ℕ and _≟_
96+
{-|
97+
Natural number artifacts.
98+
Each number is treated as a separate artifact.
99+
The size of all artifacts is zero.
100+
-}
101+
NAT' : 𝔸
102+
NAT' = record
103+
{ atoms =
104+
; atomsEqual? = ℕ._≟_
105+
; atomSize = const zero
106+
}

src/Vatras/Framework/Variants.agda

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ module Vatras.Framework.Variants where
77

88
open import Data.List using (List; []; _∷_; map)
99
open import Data.Maybe using (nothing; just)
10-
open import Data.Product using (_,_; proj₁; proj₂)
10+
open import Data.Product using (_×_; _,_; proj₁; proj₂)
1111
open import Data.String using (String; _++_; intersperse)
1212
open import Data.Unit using (⊤; tt)
1313
open import Relation.Binary.PropositionalEquality as Eq using (_≡_; _≗_; refl)
@@ -16,7 +16,7 @@ open Eq.≡-Reasoning
1616
open import Function using (id; _∘_; flip)
1717
open import Size using (Size; ↑_; ∞)
1818

19-
open import Vatras.Framework.Definitions using (𝕍; 𝔸; atoms)
19+
open import Vatras.Framework.Definitions using (𝕍; 𝔸; atoms; atomsEqual?)
2020
open import Vatras.Framework.VariabilityLanguage
2121
open import Vatras.Framework.Compiler using (LanguageCompiler)
2222
open LanguageCompiler
@@ -77,11 +77,17 @@ GrulerVL = Variant-is-VL GrulerVariant
7777
RoseVL : VariabilityLanguage (Rose ∞)
7878
RoseVL = Variant-is-VL (Rose ∞)
7979

80+
{-|
81+
Lemma to conclude that the child lists of two equal rose trees must be equal as well.
82+
-}
83+
Rose-injective : {A : 𝔸} {a₁ a₂ : atoms A} {cs₁ cs₂ : List (Rose ∞ A)} a₁ -< cs₁ >- ≡ a₂ -< cs₂ >- (a₁ ≡ a₂) × (cs₁ ≡ cs₂)
84+
Rose-injective refl = refl , refl
85+
8086
{-|
8187
Lemma to conclude that the child lists of two equal rose trees must be equal as well.
8288
-}
8389
children-equality : {A : 𝔸} {a₁ a₂ : atoms A} {cs₁ cs₂ : List (Rose ∞ A)} a₁ -< cs₁ >- ≡ a₂ -< cs₂ >- cs₁ ≡ cs₂
84-
children-equality refl = refl
90+
children-equality = proj₂ ∘ Rose-injective
8591

8692
{-|
8793
Show function for rose trees.
@@ -149,6 +155,6 @@ open import Data.Bool using (Bool; true)
149155
open import Data.Bool.ListAction using (or)
150156

151157
has-atom : {A i} atoms A Rose i A Bool
152-
has-atom {A , _≟_} a (b -< cs >-) with a ≟ b
158+
has-atom {A} a (b -< cs >-) with atomsEqual? A a b
153159
... | yes refl = true
154160
... | no x = or (map (has-atom b) cs)

src/Vatras/Lang/2CC/Encode.agda

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
open import Vatras.Framework.Definitions using (𝔽)
2+
module Vatras.Lang.2CC.Encode {Dimension : 𝔽} where
3+
4+
open import Data.List as List using (List; []; _∷_)
5+
6+
open import Size using (∞)
7+
open import Data.Bool using (false)
8+
open import Data.Unit using (⊤; tt)
9+
open import Data.List.Properties using (map-∘; map-id; map-cong)
10+
open import Relation.Binary.PropositionalEquality as Eq using (_≡_; refl)
11+
12+
open import Vatras.Data.EqIndexedSet using (_≅[_][_]_; irrelevant-index-≅)
13+
open import Vatras.Framework.Variants as V using (Rose; Variant-is-VL; VariantEncoder)
14+
open import Vatras.Framework.Relation.Function using (_⇔_; to; from)
15+
open import Vatras.Framework.VariabilityLanguage using (Semantics; Config)
16+
open import Vatras.Lang.2CC Dimension
17+
18+
encode : {i} {A} Rose i A 2CC ∞ A
19+
encode (a V.-< cs >-) = a -< List.map encode cs >-
20+
21+
confs : ⊤ ⇔ Config 2CCL
22+
confs = record
23+
{ to = λ where tt _ false
24+
; from = λ _ tt
25+
}
26+
27+
2cc-encode-idemp : {A} (v : Rose ∞ A) (c : Configuration) ⟦ encode v ⟧ c ≡ v
28+
2cc-encode-idemp {A} v@(a V.-< cs >-) c =
29+
begin
30+
⟦ encode v ⟧ c
31+
≡⟨⟩
32+
a V.-< List.map (λ x ⟦ x ⟧ c) (List.map encode cs) >-
33+
≡⟨ Eq.cong (a V.-<_>-) (map-∘ cs) ⟨
34+
a V.-< List.map (λ x ⟦ encode x ⟧ c) cs >-
35+
≡⟨ Eq.cong (a V.-<_>-) (go cs) ⟩
36+
v
37+
38+
where
39+
open Eq.≡-Reasoning
40+
41+
go : (cs' : List (Rose ∞ A)) List.map (λ c' ⟦ encode c' ⟧ c) cs' ≡ cs'
42+
go [] = refl
43+
go (c' ∷ cs') = Eq.cong₂ _∷_ (2cc-encode-idemp c' c) (go cs')
44+
45+
preserves : {A} (v : Rose ∞ A)
46+
Semantics (Variant-is-VL (Rose ∞)) v ≅[ to confs ][ from confs ] ⟦ encode v ⟧
47+
preserves {A} v = irrelevant-index-≅ v
48+
(λ { tt refl })
49+
(2cc-encode-idemp v)
50+
(to confs)
51+
(from confs)
52+
53+
encoder : VariantEncoder (Rose ∞) 2CCL
54+
encoder = record
55+
{ compile = encode
56+
; config-compiler = λ _ confs
57+
; preserves = preserves
58+
}
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
{-|
2+
This module shows that artifacts of choice calculus expressions have a fixed number of children.
3+
Afterwards, we introduce some more usable lemmas on top og this insight.
4+
-}
5+
open import Vatras.Framework.Definitions using (𝔽; 𝔸; atoms)
6+
module Vatras.Lang.2CC.FixedArtifactLength (Dimension : 𝔽) (A : 𝔸) where
7+
8+
open import Data.Bool using (true; false)
9+
open import Data.Empty using (⊥-elim)
10+
open import Data.List as List using (List; []; _∷_; _++_)
11+
import Data.List.Properties as List
12+
open import Data.List.Relation.Ternary.Interleaving.Propositional using (Interleaving; []; consˡ; consʳ)
13+
open import Data.List.Relation.Unary.All as All using (All; []; _∷_)
14+
import Data.List.Relation.Unary.All.Properties
15+
open import Data.List.Relation.Unary.AllPairs as AllPairs using (AllPairs; []; _∷_)
16+
open import Data.Nat as ℕ using (ℕ; suc; _+_; _∸_; _*_; _≤_; z≤n; s≤s; _≥_)
17+
import Data.Nat.Properties as ℕ
18+
open import Data.Product using (_×_; _,_; proj₂; ∃-syntax)
19+
open import Function using (_∘_; const)
20+
open import Relation.Binary.PropositionalEquality as Eq using (refl; _≡_; _≢_)
21+
open import Size using (Size; ∞)
22+
23+
import Vatras.Util.List as List
24+
open import Vatras.Data.EqIndexedSet using (_∈_; _⊆_)
25+
open import Vatras.Framework.Variants using (Rose; children-equality)
26+
open import Vatras.Lang.2CC Dimension using (2CC; _⟨_,_⟩; _-<_>-; ⟦_⟧)
27+
open import Vatras.Lang.2CC.ReflectsVariantSize using (reflectsVariantSize)
28+
open import Vatras.Succinctness.Sizes using (sizeRose; size2CC)
29+
30+
_≉_ : Rose ∞ A Rose ∞ A Set
31+
(a₁ Rose.-< cs₁ >-) ≉ (a₂ Rose.-< cs₂ >-) = List.length cs₁ ≢ List.length cs₂
32+
33+
{-|
34+
The key insight of this module:
35+
Given a choice calculus expression with an artifact at the root,
36+
all expressed variants must have the same number of children.
37+
-}
38+
fixedChildCount : {i}
39+
{a₁ : atoms A} {cs₁ : List (Rose ∞ A)}
40+
{a₂ : atoms A} {cs₂ : List (2CC i A)}
41+
(a₁ Rose.-< cs₁ >-) ∈ ⟦ a₂ -< cs₂ >- ⟧
42+
List.length cs₁ ≡ List.length cs₂
43+
fixedChildCount {cs₁ = cs₁} {cs₂ = cs₂} (c , v≡e) =
44+
List.length cs₁
45+
≡⟨ Eq.cong List.length (children-equality v≡e) ⟩
46+
List.length (List.map (λ e ⟦ e ⟧ c) cs₂)
47+
≡⟨ List.length-map (λ e ⟦ e ⟧ c) cs₂ ⟩
48+
List.length cs₂
49+
50+
where
51+
open Eq.≡-Reasoning
52+
53+
{-|
54+
We can partition a list of variants
55+
on whether we can choose the left or right alternative of a choice
56+
in order to configure each variant.
57+
-}
58+
partition : {i : Size}
59+
(D : Dimension) (c₁ c₂ : 2CC i A)
60+
(vs : List (Rose ∞ A))
61+
AllPairs _≉_ vs
62+
All (_∈ ⟦ D 2CC.⟨ c₁ , c₂ ⟩ ⟧) vs
63+
∃[ vs₁ ] ∃[ vs₂ ]
64+
Interleaving vs₁ vs₂ vs
65+
× All (_∈ ⟦ c₁ ⟧) vs₁
66+
× All (_∈ ⟦ c₂ ⟧) vs₂
67+
partition D c₁ c₂ [] unique-vs vs⊆e = [] , [] , [] , [] , []
68+
partition D c₁ c₂ (v ∷ vs) (v∉vs ∷ unique-vs) ((c , v≡e) ∷ vs⊆e)
69+
with partition D c₁ c₂ vs unique-vs vs⊆e
70+
... | vs₁ , vs₂ , partition , vs₁⊆e , vs₂⊆e
71+
with c D
72+
... | true = v ∷ vs₁ , vs₂ , consˡ partition , (c , v≡e) ∷ vs₁⊆e , vs₂⊆e
73+
... | false = vs₁ , v ∷ vs₂ , consʳ partition , vs₁⊆e , (c , v≡e) ∷ vs₂⊆e
74+
75+
{-|
76+
Gives a lower bound on the size of a choice calculus expression
77+
given that it expresses a number of variants with pairwise different child count.
78+
-}
79+
sum≤size2CC : {i : Size}
80+
(e : 2CC i A)
81+
(vs : List (Rose ∞ A))
82+
AllPairs (_≉_) vs
83+
All (_∈ ⟦ e ⟧) vs
84+
List.sum (List.map sizeRose vs) ≤ size2CC e
85+
sum≤size2CC (a -< cs >-) [] unique-vs vs⊆e = z≤n
86+
sum≤size2CC (a -< cs >-) (v ∷ []) unique-vs (v∈e ∷ []) =
87+
begin
88+
List.sum (List.map sizeRose (v ∷ []))
89+
≡⟨⟩
90+
sizeRose v + 0
91+
≡⟨ ℕ.+-identityʳ (sizeRose v) ⟩
92+
sizeRose v
93+
≤⟨ reflectsVariantSize v (a -< cs >-) v∈e ⟩
94+
size2CC (a -< cs >-)
95+
96+
where
97+
open ℕ.≤-Reasoning
98+
sum≤size2CC (a -< cs >-) ((a₁ Rose.-< cs₁ >-) ∷ (a₂ Rose.-< cs₂ >-) ∷ vs) ((v₁≢v₂ ∷ v₁∉vs) ∷ unique-vs) (v₁∈e ∷ v₂∈e ∷ vs⊆e) =
99+
⊥-elim (v₁≢v₂ (Eq.trans (fixedChildCount v₁∈e) (Eq.sym (fixedChildCount v₂∈e))))
100+
sum≤size2CC (D ⟨ c₁ , c₂ ⟩) vs unique-vs vs⊆e with partition D c₁ c₂ vs unique-vs vs⊆e
101+
... | vs₁ , vs₂ , partition , vs₁⊆c₁ , vs₂⊆c₂ =
102+
begin
103+
List.sum (List.map sizeRose vs)
104+
≡⟨ List.sum-Interleaving (List.map-Interleaving partition) ⟨
105+
List.sum (List.map sizeRose vs₁) + List.sum (List.map sizeRose vs₂)
106+
≤⟨ ℕ.+-mono-≤ (sum≤size2CC c₁ vs₁ (List.AllPairs-resp-⊆ (List.Interleaving⇒Sublistˡ partition) unique-vs) vs₁⊆c₁) (sum≤size2CC c₂ vs₂ (List.AllPairs-resp-⊆ (List.Interleaving⇒Sublistʳ partition) unique-vs) vs₂⊆c₂) ⟩
107+
size2CC c₁ + size2CC c₂
108+
<⟨ ℕ.n<1+n (size2CC c₁ + size2CC c₂) ⟩
109+
size2CC (D ⟨ c₁ , c₂ ⟩)
110+
111+
where
112+
open ℕ.≤-Reasoning
113+
114+
{-|
115+
Gives a lower bound on the size of a choice calculus expression
116+
given that it expresses a number of variants with pairwise different child count.
117+
In contrast to `sum≤size2CC`, this lemma is a simplified special case
118+
which makes use of a lower bound on the variant size.
119+
-}
120+
different-children-counts :
121+
{i : Size}
122+
(n : ℕ)
123+
(e : 2CC i A)
124+
(vs : List (Rose ∞ A))
125+
All (_∈ ⟦ e ⟧) vs
126+
All (λ v sizeRose v ≥ n) vs
127+
AllPairs _≉_ vs
128+
size2CC e ≥ List.length vs * n
129+
different-children-counts n e vs vs⊆e vs≥n unique-vs =
130+
begin
131+
List.length vs * n
132+
≡⟨ List.sum-replicate (List.length vs) n ⟨
133+
List.sum (List.replicate (List.length vs) n)
134+
≡⟨ Eq.cong List.sum (List.map-const n vs) ⟨
135+
List.sum (List.map (const n) vs)
136+
≤⟨ List.sum-map-≤-with∈ vs (λ v v∈vs All.lookup vs≥n v∈vs) ⟩
137+
List.sum (List.map sizeRose vs)
138+
≤⟨ sum≤size2CC e vs unique-vs (vs⊆e) ⟩
139+
size2CC e
140+
141+
where
142+
open ℕ.≤-Reasoning

0 commit comments

Comments
 (0)