Repeated query of size of type in argument list

The topic probably needs renaming.

I use a heavily overloaded routine to convert from either a generic floating point type, i.e. real(w) or **imag(**w) to complex(2*w), complex(w) to complex(w), or the polar form of a complex number, a tuple of two elements, one real(w) and one imag(w). I noticed in the latter I had to do

inline proc cmplx((x , y) : (real(?w), imag(?w)))
{
    return x * exp(y);
}

which demands that I use ?w in both elements of the tuple. I can understand that both types are being interrogated for their size simultaneously so neither knows about the size of the other at the time. Hence why both need a question mark. I was amazed that Chapel figured I was talking about the same w in both places. This might need to be documented.

As soon as I try and use w within the routine, it tells me it is not declared.

So, something is still wrong. Any ideas? Thanks

Hi @damianmoz - I think that you are encountering buggy behavior on the part of the compiler here -- in particular, I don't think it works today to use both the tuple unpacking formal and the type query. I've gone ahead and created issue Problem combining tuple unpacking formal and type queries · Issue #23302 · chapel-lang/chapel · GitHub to serve as a bug report.

Here's a little investigation of several ways to write this; the first is what you had (and doesn't work); the second is another option, but also doesn't work. The third can serve as a workaround until the others work.

use Math;

//inline proc cmplx1((x , y) : (real(?w), imag(?w)))
//{
//  writeln(w); // error: w undeclared [should be an error about w being redeclared]
//  return x * exp(y);
//}

// error: unable to resolve type
//inline proc cmplx((x , y)) where isRealType(x.type) && isImagType(y.type)
//{
//    return x * exp(y);
//}

// this version seems to work
// note: you could add `numBits(tup(0).type) == numBits(tup(1).type)`
//       if you want the real and imag to be the same width.
inline proc cmplx(tup)
  where isTuple(tup) && isRealType(tup(0).type) && isImagType(tup(1).type)
{
  const (x,y) = tup;
  return x * exp(y);
}

writeln(cmplx( (1.0, 40.0i) ));
1 Like

Hi Damian —

Michael beat me to this by a few minutes, but here are a few other notes pertaining to the original expression:

Normally, when you want to have two arguments with the same width, you wouldn't unify them to the same w using two ?w queries (which, as Chapel is defined today, would define two instances of the same w identifier within the same scope, which should generate an error); instead, you would query one or both and assert the other is the same using one of a few approaches:

The first is to query the first argument's width and constrain the second's to be the same:

inline proc cmplx(x: real(?w) , y: imag(w)) {
    return x * exp(y);
}

The other is to query the widths of both and assert that they match:

inline proc cmplx(x: real(?w) , y: imag(?w2)) where w == w2 {
    return x * exp(y);
}

The subtle difference between these is that if the first argument was a real(64), say, the first expression would be like declaring:

inline proc cmplx(x: real(64) , y: imag(64)) {
    return x * exp(y);
}

which would mean that you could pass anything to y that could be passed to an imag(64), including an imag(32).

In contrast, passing a real(64) to the second approach ought to require the second argument to be an imag(64) as well:

inline proc cmplx(x: real(64) , y: imag(?w2)) where 64 == w2 {
    return x * exp(y);
}

In which case an imag(32) y argument would not be legal.

Note, however, that in Chapel 1.31 and earlier, this version of the routine would actually accept imag(32) without warning because we proactively interpreted any built-in scalar argument type of the form t(?w) as meaning "Stamp out a version of this routine for all legal forms of t(w)." As a result, one of the overloads we'd stamp out would be:

inline proc cmplx(x: real(64) , y: imag(64) where 64 == 64 {
    return x * exp(y);
}

and this version would be called.

This behavior is the same in Chapel 1.32, but now generates a warning to indicate that in future releases we'll be changing the behavior to match what I've described above. I.e., passing in a real(64) and imag(32) would correspond to this call:

inline proc cmplx(x: real(64) , y: imag(32) where 64 == 32 {
    return x * exp(y);
}

which would not resolve due to the where-clause being false.

Today, unfortunately, the way to get this behavior would be to explicitly stamp out the two overloads you want manually:

inline proc cmplx(x: real(32) , y: imag(32) {
    return x * exp(y);
}

inline proc cmplx(x: real(64) , y: imag(64) {
    return x * exp(y);
}

Hope this is clear and makes sense,
-Brad

Thanks Brad,

If I have just two floating point numbers, then your example

inline proc cmplx(x: real(?w) , y: imag(w))
{
     return x * exp(y);
}

is the way I would do it.

The other way that you mention is to query the widths of both and assert that they match:

inline proc cmplx(x: real(?w) , y: imag(?w2)) where w == w2
{
    return x * exp(y);
}

Not quite my style although you will see from my Github post that I have tried that.

My issue is that I have a tuple which I want to retain as a tuple (and not split up). And as soon as you try either of

inline proc cmplx((x: real(?w) , y: imag(w)))
inline proc cmplx((x: real(?w) , y: imag(w2))) where

you see problems. See my Github post for the details.

In my case, I have the tuple (x, y) which is the polar form of a complex number, the first being a real number and the second an imaginary number. I can feed it (without messing about) to the complex exponential function of Chapel to yield the reverse mapping to a complex number from the polar form:

x * exp(y) = (x * cos(y)) + i (x * sin(y)) // how do I make that 'i' bold in this Chapel block

I did not want to have to split the split the tuple coming from the routine which produced the polar form before feeding it to my cmplx routine. I want the argument to be a tuple.

Any operation of converting two arbitrary w-bit floating point numbers x and y, the first real and the second imaginary is a totally different operation. Because both of those are mathematically a complex number anyway, albeit the first with a zero-valued imaginary part and the second with a zero-valued real part, their combination into a complex number is just their sum (x+y) which has the value

(x, y:real(w)):complex(2*w)

So, it is not quite the same thing. I need an overload routine with a tuple argument.

Thanks again.

1 Like