Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support using complex numbers and lists of complex numbers inside kernel functions #1518

Closed

Conversation

annagrin
Copy link
Collaborator

@annagrin annagrin commented Apr 15, 2024

Make sure we can use complex numbers inside kernel function definitions, for example:

  c = [.70710678 + 0j, 0., 0., 0.70710678]
  
  # Pass a list of complex numbers as a parameter
  @cudaq.kernel
  def test_complex_vec_param(vec : list[complex]):
      c1 = vec

  counts = cudaq.sample(test_complex_vec_param, c)
  # Capture a list of complex numbers
  @cudaq.kernel
  def test_complex_vec_capture():
      c1 = c

   counts = cudaq.sample(test_complex_vec_capture)
  # Define a list of complex numbers inside a kernel
  @cudaq.kernel
  def test_complex_vec_definition():
      c1 = [1.0 + 0j, 0., 0., 1.]

  counts = cudaq.sample(test_complex_vec_definition)

Towards: #1086
Closes: #1454

Copy link

copy-pr-bot bot commented Apr 15, 2024

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

Copy link

github-actions bot commented Apr 15, 2024

CLA Assistant Lite bot All Contributors have signed the CLA.

@annagrin
Copy link
Collaborator Author

I have read the Contributor License Agreement and I hereby accept the Terms.

@annagrin
Copy link
Collaborator Author

recheck

"""
ty = self.getFloatType()
return arith.ConstantOp(ty, self.getFloatAttr(ty, value)).result

def getComplexType(self):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be good to parameterize this with the precision or bitwidth in MLIR terms (32 or 64 is what we support)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

"""
return ComplexType.get(self.getFloatType())

def getConstantComplex(self, value):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here (parameterize with 32 or 64)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@@ -2726,6 +2730,22 @@ def visit_Continue(self, node):
else:
cc.ContinueOp([])

def promote_operand_type(self, ty, operand):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we move this up with the other utility methods on this visitor class?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@@ -2896,16 +2906,21 @@ def visit_Name(self, node):

if node.id in self.capturedVars:
# Only support a small subset of types here
complexType = type(1j)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can just use complex instead of getting the type of 1j

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also probably check for np.complex64

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can just use complex instead of getting the type of 1j

We use complex dialect and it seems to override the complex type. Maybe worth decoupling in a separate PR...

We should also probably check for np.complex64

Done.

def test_float_vec_param(vec : list[float]):
f1 = vec

counts = cudaq.sample(test_float_vec_param, f)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A better test here would be to define a complex return type on the kernel, and assert test_float_vec_param(f) == that value

something like

@cudaq.kernel
def test_float_vec_param(vec : list[float], i : int) -> complex: 
    f1 = vec 
    return vec[i]

for i in range(len(f)): 
   assert np.isclose(f[i], test_float_vec_param(f, i), atol=1e-6) 

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opps, looks like there is a bug there - function above always returns the value of f[0]. Will investigate...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed the tests for now to work around this issue (will update the tests after the issue is fixed in the experimental branch as well)

counts = cudaq.sample(test_float_np_use)
assert len(counts) == 0

def test_complex():
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest also adding support for c = np.array([...], dtype=np.complex64) and demonstrate with the nvidia target. Also it would be good to test that we raise an exception for the use of complex with nvidia and np.complex64 with FP64 backends (all the other ones).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created a separate issue for supporting np arrays: #1523, will work in it in the next PR

@annagrin annagrin requested a review from schweitzpgi April 16, 2024 22:40
"""
Return an MLIR complex type (double precision).
"""
return ComplexType.get(self.getFloatType())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q: Is there a way to request either F32 or F64 in the python bindings? (This getFloatType() feels more ambiguous.)


if F64Type.isinstance(ty):
if IntegerType.isinstance(operand.type):
operand = arith.SIToFPOp(ty, operand).result
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These conversions make me wonder if we don't want to automatically add FPTrunc and FPExt if the input data is not at the precision expected by the simulator.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to postpone this to the next PR(s) when I have examples that are running with different data types in np.array and assigning the state to a qvector (to be able to tests my changes properly) - does it is sound reasonable?

@annagrin
Copy link
Collaborator Author

Replaced by #1550

@annagrin annagrin closed this Apr 19, 2024
@github-actions github-actions bot locked and limited conversation to collaborators Apr 19, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants