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

Fix root cause of bug reported in PR #1412 #1417

Merged
merged 2 commits into from
Aug 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions src/qibo/backends/numpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -645,9 +645,7 @@ def aggregate_shots(self, shots):
return self.cast(shots, dtype=shots[0].dtype)

def samples_to_binary(self, samples, nqubits):
### This is faster just staying @ NumPy.
qrange = np.arange(nqubits - 1, -1, -1, dtype=np.int32)
samples = self.to_numpy(samples)
return np.mod(np.right_shift(samples[:, None], qrange), 2)
Copy link
Member

Choose a reason for hiding this comment

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

Aren't all the other operations in here still NumPy? Or is Torch automatically converting NumPy calls?

I expected self.np. usage, but instead there is np. everywhere...

Copy link
Contributor Author

@renatomello renatomello Aug 8, 2024

Choose a reason for hiding this comment

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

I tried self.np and it creates other issues because torch.arange and torch.right_shift work slightly differently

Copy link
Member

Choose a reason for hiding this comment

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

But is np.right_shift() able to work on Torch tensors? (i.e. taking Torch tensors as input and returning them as output)

I see it could be vaguely possible, but it's still good to know, because I wouldn't have been confident to say it's actually able.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In [6]: import numpy as np
   ...: import torch
   ...: 
   ...: x = torch.Tensor(torch.arange(10))
   ...: qrange = np.arange(10 - 1, -1, -1, dtype=np.int32)
   ...: type(np.right_shift(x[:, None], qrange))
Out[6]: torch.Tensor

Copy link
Member

Choose a reason for hiding this comment

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

Nice, thanks :)

Copy link
Member

@alecandido alecandido Aug 8, 2024

Choose a reason for hiding this comment

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

It seems that by now this starts being reliable

https://numpy.org/doc/stable/user/basics.interoperability.html#operating-on-foreign-objects-without-converting
https://numpy.org/neps/nep-0018-array-function-protocol.html#nep18

(ok, most likely since a while, but it is for sure framework-dependent, I should take the time of assessing which ones are fully supporting it)


def samples_to_decimal(self, samples, nqubits):
Expand Down
11 changes: 6 additions & 5 deletions src/qibo/measurements.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,11 +167,12 @@ def samples(self, binary=True, registers=False):
# calculate samples for the whole circuit so that
# individual register samples are registered here
self.circuit.final_state.samples()

if binary:
return self._samples
else:
qubits = self.measurement_gate.target_qubits
return self.backend.samples_to_decimal(self._samples, len(qubits))

qubits = self.measurement_gate.target_qubits
return self.backend.samples_to_decimal(self._samples, len(qubits))

def frequencies(self, binary=True, registers=False):
"""Returns the frequencies of measured samples.
Expand All @@ -198,8 +199,8 @@ def frequencies(self, binary=True, registers=False):
if binary:
qubits = self.measurement_gate.target_qubits
return frequencies_to_binary(self._frequencies, len(qubits))
else:
return self._frequencies

return self._frequencies

def apply_bitflips(self, p0, p1=None): # pragma: no cover
return apply_bitflips(self, p0, p1)
7 changes: 1 addition & 6 deletions src/qibo/result.py
Original file line number Diff line number Diff line change
Expand Up @@ -325,12 +325,7 @@ def samples(self, binary: bool = True, registers: bool = False):
if self._samples is None:
if self.measurements[0].result.has_samples():
self._samples = self.backend.np.concatenate(
[
self.backend.cast(
gate.result.samples(), dtype=self.backend.np.int32
)
for gate in self.measurements
],
[gate.result.samples() for gate in self.measurements],
axis=1,
)
else:
Expand Down