Skip to content

Commit 2748ab7

Browse files
committed
Example
1 parent d1824d6 commit 2748ab7

1 file changed

Lines changed: 245 additions & 1 deletion

File tree

README.md

Lines changed: 245 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,245 @@
1-
# How to use isl via the C++ / Python interface
1+
Warning: This is a proposal, which does not document the current isl
2+
3+
# How to use isl via the C++ and Python interface
4+
5+
## Constructing an integer set or map (isl::set / isl::map)
6+
7+
### Explicit Interface (today)
8+
9+
We first describe how the current C++ interface should be used to construct
10+
isl sets and maps, just proposing a small number of extensions beyond what exists
11+
today. The resulting code is still somehow verbose, but very explicit.
12+
13+
Example:
14+
15+
*{ [N, M] -> { [i,j] : 2 * M + 3 * N <= 2 * i + j + 10 }*
16+
17+
We create an integer set as follows. We first create a set of identifiers for
18+
each of the needed dimensions (`isl::id Id_N(ctx, "N"`). We then introduce
19+
initial expressions for all identifiers and required constants (`isl::pw_aff
20+
N(Id_N), Ten(ctx, 10)`). From these initial expressions the actual affine
21+
expressions are constructed (`isl::pw_aff LHS = Two.mul(M)`). Pairs of
22+
expressions are combined with the operators lt_set (<), le_set (<=), ge_set
23+
(>=), gt_set (>), eq_set (=), ne_set (!=) into a parameteric set of constraints
24+
(`isl::set PSet = LHS.le_set(RHS)`). Finally, a non-parameteric set is
25+
constructed from 1) a parameteric set specifying its constraints and 2) a list
26+
of identifiers that specify the parameter dimensions that should be promoted to
27+
set dimensions (`isl::set Set({Id_i, Id_j}, PSet)`). Similary, a map can be
28+
constructed by providing two lists of identifiers defining the input and output
29+
dimensions (`isl::map Map({Id_i}, {Id_j}, PSet)`)
30+
31+
32+
33+
34+
```
35+
// Identifiers
36+
isl::id Id_N(ctx, "N"), Id_M(ctx, "M"), Id_i(ctx, "i"), Id_j(ctx, "j");
37+
38+
// One (piece-wise) affine expression per identifier
39+
// [N] -> { [(N)]}, [N] -> { [(M)]}, [i] -> { [(i)]}, [j] -> { [(j)]}
40+
isl::pw_aff N(Id_N), M(Id_M), i(Id_i), j(Id_j);
41+
42+
// One (piece-wise) affine expression per constant
43+
// {[(10)]}, {[(2)]}, {[(3)]}
44+
isl::pw_aff Ten(ctx, 10), Two(ctx, 2), Three(ctx, 3);
45+
46+
// Build the left and right hand side of the expression
47+
// [M, N] -> { [(2 * M + 3 * M)] }
48+
isl::pw_aff LHS = Two.mul(M).add(Three.mul(N));
49+
50+
// [M, N] -> { [(2 * i + j + 10)] }
51+
isl::pw_aff RHS = Two.mul(i).add(j).add(Ten);
52+
53+
// [N, M, i, j] -> { : 2 * M + 3 * N <= 2 * i + j + 10 }
54+
isl::set PSet = LHS.le_set(RHS);
55+
56+
// [N, M] -> { [i, j] : 2 * M + 3 * N <= 2 * i + j + 10 }
57+
isl::set Set({Id_i, Id_j}, PSet);
58+
59+
// [N, M] -> { [i] -> [j] : 2 * M + 3 * N <= 2 * i + j + 10 }
60+
isl::map Map({Id_i}, {Id_j}, PSet);
61+
```
62+
63+
#### New functions
64+
65+
```
66+
__isl_constructor
67+
isl_pw_aff *isl_pw_aff_param_on_domain_id(isl_id *identifier);
68+
__isl_constructor
69+
isl_pw_aff *isl_pw_aff_const_on_domain_val(isl_val *value);
70+
__isl_constructor
71+
isl_pw_aff *isl_pw_aff_const_on_domain_si(long *value);
72+
__isl_constructor
73+
isl_set *isl_set_from_id_list_and_set(isl_id_list *dims, isl_set *pset);
74+
__isl_constructor
75+
isl_map *isl_map_from_id_list_and_set(isl_id_list *input_dims, isl_id_list *output_dims, isl_set *pset);
76+
```
77+
78+
#### Notes
79+
80+
- Currently instead of isl::aff, we always need to use isl::pw_aff, as
81+
isl::aff does not allow for parameter auto-alignment. This should be
82+
changed, but will require more work. We should likely write the
83+
documentation in terms of isl::pw_aff for now.
84+
85+
#### Choices
86+
87+
##### Return type of the comparision operator
88+
89+
There have been concerns that the return type of the `le_set` expression
90+
should not be a set, but rather a constraint.
91+
92+
There are currently three options:
93+
94+
- Sets:
95+
96+
```
97+
// [N, M, i, j] -> { : 2 * M + 3 * N <= 2 * i + j + 10 }
98+
isl::set PSet = LHS.le_set(RHS);
99+
isl::set Set({i}, PSet);
100+
```
101+
102+
- Constraints:
103+
104+
```
105+
// [N, M, i, j] -> { : 2 * M + 3 * N <= 2 * i + j + 10 }
106+
isl::constraint PSet = LHS.le_set(RHS);
107+
isl::set Set({i}, PSet);
108+
```
109+
110+
This has the benefit that there is a clear separation between the result of
111+
`le_set` and the construction of the set.
112+
113+
This approach has the drawback that a `pw_aff` cannot be translated into
114+
a constraint, so for translating `pw_aff`'s to sets another interface would
115+
be needed.
116+
117+
- Marker
118+
119+
```
120+
121+
// [N, M, i, j] -> { : 2 * M + 3 * N <= 2 * i + j + 10 }
122+
isl::set PSet = isl::set_maker(LHS) <= RHS;
123+
isl::set Set({i}, PSet);
124+
```
125+
126+
TODO: Alex, can you suggest a description of what the benefit of this approach
127+
are.
128+
129+
##### How to introduce parameters
130+
131+
We can either use a constructor (in the proposal):
132+
133+
```
134+
isl::set PSet("[N, M, i, j] -> { : 2 * M + 3 * N <= 2 * i + j + 10 }")
135+
136+
// { [N, M] -> { [i,j] : 2 * M + 3 * N <= 2 * i + j + 10 }
137+
isl::set Set({i,j}, PSet);
138+
// { [N, M] -> { [i]->[j] : 2 * M + 3 * N <= 2 * i + j + 10 }
139+
isl::map Map({i}, {j}, PSet);
140+
```
141+
142+
or a set of member functions.
143+
144+
```
145+
isl::set PSet("[N, M, i, j] -> { : 2 * M + 3 * N <= 2 * i + j + 10 }")
146+
147+
// { [N, M] -> { [i,j] : 2 * M + 3 * N <= 2 * i + j + 10 }
148+
isl::set PSet.inputs(i,j);
149+
// { [N, M] -> { [i]->[j] : 2 * M + 3 * N <= 2 * i + j + 10 }
150+
isl::map PSet.inputs(i).outputs(j);
151+
```
152+
153+
### Streamlined interface (future)
154+
155+
In certain use cases a more streamlined interface might be useful. Here is
156+
an example which includes:
157+
158+
- operator overloading
159+
- automatic conversion from isl::id to isl::aff
160+
- automatic conversion from int to isl::aff
161+
- a default context
162+
163+
```
164+
isl::id N("N"), M("M"), i("i"), j("j');
165+
166+
isl::aff LHS = 2 * M + 3 * N; // natural precedence works
167+
isl::aff RHS = 2 * i + j + 10;
168+
169+
// { [N, M] -> { [i,j] : 2 * M + 3 * N <= 2 * i + j + 10 }
170+
isl::set Set({i,j}, LHS <= RHS);
171+
172+
// { [N, M] -> { [i]->[j] : 2 * M + 3 * N <= 2 * i + j + 10 }
173+
isl::map Map({i}, {j}, LHS <= RHS);
174+
```
175+
176+
#### Extensions
177+
178+
##### Use of a thread-local context
179+
180+
Instead of always providing a ctx object, the bindings could provide a thread
181+
local ctx.
182+
183+
184+
Explicit context:
185+
```
186+
isl::id N(ctx, "N");
187+
```
188+
189+
Implicit context:
190+
```
191+
isl::id N("N");
192+
```
193+
194+
##### Overloading of operators
195+
196+
Instead of calling the explicit interface, operator overloading can be used.
197+
198+
Without overloading:
199+
```
200+
isl::pw_aff = A.add(B).add(Three.mul(C));
201+
```
202+
203+
With overloading
204+
```
205+
isl::pw_aff = A + B + 3 * C;
206+
```
207+
208+
*Warning*: Overloading of the comparision operators may cause confusion as the
209+
result is not a boolean expression.
210+
211+
A solution might be to have these operators in a separate sub-namespace to
212+
avoid surprising behavior of operator overloads.
213+
214+
#### Choices
215+
216+
#### More efficient construction of parameter isl::aff's
217+
218+
When constructing an affine expression for a parameter, the explicit interface
219+
requires two steps. First the construction of an isl::id and then its conversion
220+
to a isl::aff. It would be nice if just one step would be needed. There
221+
are two options:
222+
223+
1) Construction of isl::aff's from strings.
224+
225+
```
226+
isl::aff A = ...
227+
228+
// [N] -> { [(N)] }
229+
isl::aff N(ctx, "N");
230+
231+
isl::aff X = A.add(N);
232+
```
233+
234+
2) Automatic conversion from isl::id to aff
235+
236+
```
237+
isl::aff A = ...
238+
239+
// [N] -> { [(N)] }
240+
isl::id N(ctx, "N");
241+
242+
isl::aff X = A.add(N);
243+
```
244+
245+

0 commit comments

Comments
 (0)