Skip to content

Commit f635c2f

Browse files
committed
Example
1 parent d1824d6 commit f635c2f

File tree

1 file changed

+263
-1
lines changed

1 file changed

+263
-1
lines changed

README.md

Lines changed: 263 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,263 @@
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_from_id(isl_id *identifier);
68+
__isl_constructor
69+
isl_pw_aff *isl_pw_aff_val_from_val(isl_val *value);
70+
__isl_constructor
71+
isl_pw_aff *isl_pw_aff_val_from_si(int value);
72+
```
73+
See: https://github.com/PollyLabs/isl/pull/25
74+
75+
```
76+
__isl_constructor
77+
isl_set *isl_set_from_id_list_params(isl_id_list *dims, isl_set *pset);
78+
__isl_constructor
79+
isl_map *isl_map_from_id_list_params(isl_id_list *input_dims, isl_id_list *output_dims, isl_set *pset);
80+
```
81+
See: https://github.com/PollyLabs/isl/pull/26
82+
83+
#### Notes
84+
85+
##### Why not use isl_aff
86+
87+
Currently we always need to use isl_pw_aff, as isl_aff does not allow for
88+
parameter auto-alignment. This should be changed, but will require more work.
89+
We should likely write the documentation in terms of isl_pw_aff for now.
90+
91+
##### How to introduce parameters
92+
93+
We can either use a constructor (in the proposal):
94+
95+
```
96+
isl::set PSet("[N, M, i, j] -> { : 2 * M + 3 * N <= 2 * i + j + 10 }")
97+
98+
// { [N, M] -> { [i,j] : 2 * M + 3 * N <= 2 * i + j + 10 }
99+
isl::set Set({i,j}, PSet);
100+
// { [N, M] -> { [i]->[j] : 2 * M + 3 * N <= 2 * i + j + 10 }
101+
isl::map Map({i}, {j}, PSet);
102+
```
103+
104+
or a set of member functions.
105+
106+
```
107+
isl::set PSet("[N, M, i, j] -> { : 2 * M + 3 * N <= 2 * i + j + 10 }")
108+
109+
// { [N, M] -> { [i,j] : 2 * M + 3 * N <= 2 * i + j + 10 }
110+
isl::set PSet.inputs(i,j);
111+
// { [N, M] -> { [i]->[j] : 2 * M + 3 * N <= 2 * i + j + 10 }
112+
isl::map PSet.inputs(i).outputs(j);
113+
```
114+
115+
### Explicit Interface (isl::aff)
116+
117+
After isl_aff supports parameter auto-alignment it will be possible to write the
118+
above example (and other examples which do not require isl_pw_affs) as follows:
119+
120+
```
121+
// Identifiers
122+
isl::id Id_N(ctx, "N"), Id_M(ctx, "M"), Id_i(ctx, "i"), Id_j(ctx, "j");
123+
124+
// One (piece-wise) affine expression per identifier
125+
// [N] -> { [(N)]}, [N] -> { [(M)]}, [i] -> { [(i)]}, [j] -> { [(j)]}
126+
isl::aff N(Id_N), M(Id_M), i(Id_i), j(Id_j);
127+
128+
// One (piece-wise) affine expression per constant
129+
// {[(10)]}, {[(2)]}, {[(3)]}
130+
isl::aff Ten(ctx, 10), Two(ctx, 2), Three(ctx, 3);
131+
132+
// Build the left and right hand side of the expression
133+
// [M, N] -> { [(2 * M + 3 * M)] }
134+
isl::aff LHS = Two.mul(M).add(Three.mul(N));
135+
136+
// [M, N] -> { [(2 * i + j + 10)] }
137+
isl::aff RHS = Two.mul(i).add(j).add(Ten);
138+
139+
// [N, M, i, j] -> { : 2 * M + 3 * N <= 2 * i + j + 10 }
140+
isl::constraint C = LHS.le_constraint(RHS);
141+
142+
// [N, M] -> { [i, j] : 2 * M + 3 * N <= 2 * i + j + 10 }
143+
isl::set Set({Id_i, Id_j}, C);
144+
145+
// [N, M] -> { [i] -> [j] : 2 * M + 3 * N <= 2 * i + j + 10 }
146+
isl::map Map({Id_i}, {Id_j}, C);
147+
```
148+
149+
#### TODOs:
150+
151+
- Introduce parameter auto-alignment to isl_aff
152+
- Introduce isl_aff [op]_constraint methods
153+
- Enable conversion from a constraint to an isl_set
154+
155+
### Streamlined interface (future)
156+
157+
In certain use cases a more streamlined interface might be useful. Here is
158+
an example which includes:
159+
160+
- operator overloading
161+
- automatic conversion from isl::id to isl::aff
162+
- automatic conversion from int to isl::aff
163+
- a default context
164+
165+
```
166+
isl::id N("N"), M("M"), i("i"), j("j');
167+
168+
isl::aff LHS = 2 * M + 3 * N; // natural precedence works
169+
isl::aff RHS = 2 * i + j + 10;
170+
171+
// { [N, M] -> { [i,j] : 2 * M + 3 * N <= 2 * i + j + 10 }
172+
isl::set Set({i,j}, LHS <= RHS);
173+
174+
// { [N, M] -> { [i]->[j] : 2 * M + 3 * N <= 2 * i + j + 10 }
175+
isl::map Map({i}, {j}, LHS <= RHS);
176+
```
177+
178+
#### Extensions
179+
180+
##### Use of a thread-local context
181+
182+
Instead of always providing a ctx object, the bindings could provide a thread
183+
local ctx.
184+
185+
186+
Explicit context:
187+
```
188+
isl::id N(ctx, "N");
189+
```
190+
191+
Implicit context:
192+
```
193+
isl::id N("N");
194+
```
195+
196+
##### Overloading of operators
197+
198+
Instead of calling the explicit interface, operator overloading can be used.
199+
200+
Without overloading:
201+
```
202+
isl::pw_aff = A.add(B).add(Three.mul(C));
203+
```
204+
205+
With overloading
206+
```
207+
isl::pw_aff = A + B + 3 * C;
208+
```
209+
210+
*Warning*: Overloading of the comparision operators may cause confusion as the
211+
result is not a boolean expression.
212+
213+
A solution might be to have these operators in a separate sub-namespace to
214+
avoid surprising behavior of operator overloads.
215+
216+
#### Choices
217+
### Other Options
218+
219+
- Marker
220+
221+
```
222+
223+
// [N, M, i, j] -> { : 2 * M + 3 * N <= 2 * i + j + 10 }
224+
isl::set PSet = isl::set_maker(LHS) <= RHS;
225+
isl::set Set({i}, PSet);
226+
```
227+
228+
TODO: Alex, can you suggest a description of what the benefit of this approach
229+
are.
230+
231+
```
232+
233+
234+
#### More efficient construction of parameter isl::aff's
235+
236+
When constructing an affine expression for a parameter, the explicit interface
237+
requires two steps. First the construction of an isl::id and then its conversion
238+
to a isl::aff. It would be nice if just one step would be needed. There
239+
are two options:
240+
241+
1) Construction of isl::aff's from strings.
242+
243+
```
244+
isl::aff A = ...
245+
246+
// [N] -> { [(N)] }
247+
isl::aff N(ctx, "N");
248+
249+
isl::aff X = A.add(N);
250+
```
251+
252+
2) Automatic conversion from isl::id to aff
253+
254+
```
255+
isl::aff A = ...
256+
257+
// [N] -> { [(N)] }
258+
isl::id N(ctx, "N");
259+
260+
isl::aff X = A.add(N);
261+
```
262+
263+

0 commit comments

Comments
 (0)