Skip to content

Conversation

@diehoq
Copy link
Contributor

@diehoq diehoq commented Sep 22, 2025

This PR introduces a set of arithmetic utility functions for QuantumFloats

New Functions Added:

  1. q_min – returns the minimum of two QuantumFloat values
  2. q_max – returns the maximum of two QuantumFloat values
  3. q_floor – computes the floor of a QuantumFloat
  4. q_ceil – computes the ceiling of a QuantumFloat
  5. q_fractional – extracts the fractional part of a QuantumFloat
  6. q_round – rounds a QuantumFloat to the nearest integer
  7. q_modf – splits a QuantumFloat into integer and fractional components

Additional Updates:

Comprehensive documentation for each new function
Corresponding unit tests

@renezander90 renezander90 self-assigned this Sep 22, 2025
for i in jrange(-a.exponent, a.size):
cx(a[i], b[i])

return b
Copy link
Contributor

Choose a reason for hiding this comment

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

By using duplicate, you create a QuantumFloat that could potentially have non-integer values. The result of floor can however be an integer only. I guess it depends on the usecase whether this is unnecessary. What do you think @renezander90 @cjn10?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've thought about this and I think that, since the operation is out of place and the initial variable still exists, it makes more sense to preserve the same structure in the output variable in order to allow operations between them.

{(2, 1, 2): 0.5, (3, 1, 3): 0.5}
"""

res = QuantumFloat(a.size)
Copy link
Contributor

@positr0nium positr0nium Sep 22, 2025

Choose a reason for hiding this comment

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

I think this can be made a bit more robust :)

res_mshape = [jnp.minimum(a.mshape[0], b.mshape[0]), jnp.maximum(a.mshape[1], b.mshape[1])]
new_msize = res_mshape[1] - res_mshape[0]
new_exponent = res_mshape[0]
res = QuantumFloat(new_msize, new_exponent)

Copy link
Contributor

@renezander90 renezander90 Sep 23, 2025

Choose a reason for hiding this comment

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

Yes :) This will now work for QuantumFloats of different shape. (But, not yet for signed.)

Could work for signed with:

res = QuantumFloat(new_msize, new_exponent, signed=a.signed | b.signed)

>>> multi_measurement([a,b,res_min])
{(2, 1, 1): 0.5, (3, 1, 1): 0.5}
"""
res = QuantumFloat(a.size)
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here as in q_max

cx(a[i], res[i])
with control(c, 0):
for i in jrange(b.size):
cx(b[i], res[i])
Copy link
Contributor

Choose a reason for hiding this comment

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

This might be more efficient like this:

cx(a, res)

with conjugate(cx)(a,b):
    with control(c, 0):
        cx(b, res)

Copy link
Contributor

Choose a reason for hiding this comment

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

But, this wouldn't work if an and b have different shape. With the proposed changes from above, this should work for QuantumFloats of different shape:

    with control(c):
        for i in jrange(a.size):
            cx(a[i], res[i + a.exponent - new_exponent])
    with control(c, 0):
        for i in jrange(b.size):
            cx(b[i], res[i + b.exponent - new_exponent])

Copy link
Contributor

Choose a reason for hiding this comment

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

The efficient approach should work on the digits where a and b overlap. On the disjoint digits, the straightforward way has to be employed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The extreme cases of the approach you suggest - no overlap and maximal overlap - will lead to a situation in which the cx is called with two empty lists of qubits. In this case the cx should not be appended to the circuit, but as of now this behaviour is simply not allowed.

compare_func = lambda x, y: x <= y

with invert():
(c << compare_func)(a, b)
Copy link
Contributor

Choose a reason for hiding this comment

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

Might be more elegant and give an in-built custom control to do this via conjugation

@positr0nium
Copy link
Contributor

Nice work! Added a few comments within the changed files :) Thanks

@diehoq diehoq reopened this Sep 30, 2025
@diehoq diehoq marked this pull request as draft October 6, 2025 11:22
@diehoq diehoq marked this pull request as ready for review October 20, 2025 09:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants