Q:

How to get the (x,y) coordinates given a index when x < y?

In the past I've often used loops of the following kind (Haskell example):
  upperBoundToTuples :: Int -> [(Int, Int)]
  upperBoundToTuples n = [(x,y) | x <- [0..n], y <- [x+1..n]]

The above code produces tuples of range (0,1)..(n,n) where for all x < y.
I was wondering if it there was an efficient way of getting those (x,y) indices given a single index? Possible applications include optimization problems on the GPU where loops are not allowed and each thread only gets an index.
Also if it is possible for the 2D case, could such an algorithm be generalized to multiple dimensions?

A:

You're asking for a bijection from [0, N(N+1)/2) to pairs (x, y) with 0 <= x < y <= N.
Here's one simple way to define it (in pseudocode, but should be trivial to convert to Haskell):
x0, y0 = i / (N + 1), i % (N + 1)
if x0 < y0 then result = (x0, y0)
else result = (N - 1 - x0, N - y0)

Here's a visualisation of the function for N=6. The map is laid out in a table with rows of length N+1=7, with the first row representing the value of the function for i=0 to 6, the next i=7 to 13 and so on. If you look very closely and carefully, you can see that things above the leading diagonal map to their own location in the table, and things on or below the diagonal map rotationally to the later entries.
5,6  0,1  0,2  0,3  0,4  0,5  0,6
4,6  4,5  1,2  1,3  1,4  1,5  1,6
3,6  3,5  3,4  2,3  2,4  2,5  2,6

And here's the opposite of this visualisation: a table T of size (N+1) by (N+1) with T[x, y] = i where i is mapped to (x, y) by the function above.
 -   1   2   3   4   5   6
 -   -   9  10  11  12  13
 -   -   -  17  18  19  20
 -   -   -   -  16  15  14
 -   -   -   -   -   8   7
 -   -   -   -   -   -   0
 -   -   -   -   -   -   -

Higher dimensions
This method can probably be made to work in higher dimensions, but I don't immediately see how. As an alternative, here's a simple but somewhat inefficient method that does work in arbitrary dimensions.
First, note there's choose(N + 1, k) increasing sequences of length k from the numbers from 0 to N (where choose(N, k) is the binomial coefficient). Of those, choose(N, k - 1) of them end with N. That gives this recursive function that generates the sequences in descending colexicographical order (again in pseudocode):
sequence(N, k, index)
    = []                                           if k == 0
    = sequence(N - 1, k - 1, index) + [N]          if index < choose(N, k - 1)
    = sequence(N - 1, k, index - choose(N, k - 1)) otherwise

Here's, sequence(5, 3, index) for index between 0 and 19:
 0 -> [3, 4, 5]
 1 -> [2, 4, 5]
 2 -> [1, 4, 5]
 3 -> [0, 4, 5]
 4 -> [2, 3, 5]
 5 -> [1, 3, 5]
 6 -> [0, 3, 5]
 7 -> [1, 2, 5]
 8 -> [0, 2, 5]
 9 -> [0, 1, 5]
10 -> [2, 3, 4]
11 -> [1, 3, 4]
12 -> [0, 3, 4]
13 -> [1, 2, 4]
14 -> [0, 2, 4]
15 -> [0, 1, 4]
16 -> [1, 2, 3]
17 -> [0, 2, 3]
18 -> [0, 1, 3]
19 -> [0, 1, 2]

