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

one hot encoding #918

Open
wants to merge 26 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
8b79536
one hot encoding
skushnir123 May 2, 2024
c1f3fb1
Update one_hot_encoding.py
skushnir123 May 2, 2024
4141ba1
nit change
skushnir123 May 7, 2024
63ca012
switched endianness
skushnir123 May 7, 2024
79d75c9
Update one_hot_encoding.py
skushnir123 May 7, 2024
2258be7
Update one_hot_encoding.py
skushnir123 May 7, 2024
e294fd3
Update one_hot_encoding.py
skushnir123 May 9, 2024
48b7e59
Update one_hot_encoding.py
skushnir123 May 9, 2024
626a4f7
added one-sided register
skushnir123 May 9, 2024
5c9447d
changing to RIGHT registers
skushnir123 May 9, 2024
9d81e17
Update one_hot_encoding.py
skushnir123 May 9, 2024
b176982
Update one_hot_encoding.py
skushnir123 May 12, 2024
9ea4a4e
reformat
skushnir123 May 12, 2024
16ff44f
Update one_hot_encoding.py
skushnir123 May 12, 2024
55cc80b
Update one_hot_encoding_test.py
skushnir123 May 12, 2024
6ba5ec0
Update one_hot_encoding_test.py
skushnir123 May 12, 2024
c192e22
Update one_hot_encoding.py
skushnir123 May 12, 2024
0e6df6b
Update one_hot_encoding_test.py
skushnir123 May 12, 2024
887950f
Update one_hot_encoding_test.py
skushnir123 May 12, 2024
fc5c373
Merge branch 'main' into one-hot-encoding
fdmalone May 15, 2024
5f5a385
Merge branch 'main' into one-hot-encoding
mpharrigan May 16, 2024
7bbfdd2
Update one_hot_encoding.py
skushnir123 May 16, 2024
8cad959
Merge branch 'one-hot-encoding' of https://github.com/skushnir123/Qua…
skushnir123 May 16, 2024
ee51728
Update one_hot_encoding.py
skushnir123 May 16, 2024
8700910
one_hot_encoding test
skushnir123 May 22, 2024
7dcb54e
Merge branch 'main' into one-hot-encoding
tanujkhattar Nov 25, 2024
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
53 changes: 53 additions & 0 deletions qualtran/bloqs/data_loading/one_hot_encoding.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Copyright 2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import attrs
import cirq
from numpy._typing import NDArray

from qualtran import GateWithRegisters, QAny, QUInt, Signature
from qualtran.bloqs.basic_gates import TwoBitCSwap


@attrs.frozen
class OneHotEncoding(GateWithRegisters):
Copy link
Collaborator

Choose a reason for hiding this comment

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

Isn't this same as the SwapWithZero gate we already have?

Copy link
Collaborator

Choose a reason for hiding this comment

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

You can essentially write a one hot encoding bloq by doing an X(q[0]) and then call SwapWithZero with a as control and b as target.
The part after the first X gate is SwapWithZero bloq.

image

Also, SwapWithZero now supports N-dimensional registers so you can also do a one-hot for a multi dimensional input / output register. For example, if you have a 3d data where the input is 3 selection registers (a_x, a_y, a_z) and target is a 3D encoding where you would set target[x][y][z] = 1 when a_x = x, a_y = y and a_z = z; you can do this again by setting target[0][0][0] = 1 and calling a 3D swap-with-zero.

Copy link
Collaborator

Choose a reason for hiding this comment

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

There's no issue with swap with zero destroying the rest of the "b" register here? I've always been confused by when SwapWithZero is ok to use.

Copy link
Collaborator

@tanujkhattar tanujkhattar May 17, 2024

Choose a reason for hiding this comment

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

The |B> register is assumed to be |0> in this case, so it doesn't matter how you permute it it will continue to be 0 everywhere except the x'th bit where you'll have a 1 since you swapped 0 <-> x .

The circuit after the first X gate is the same circuit as used for SwapWithZero

Copy link
Collaborator

Choose a reason for hiding this comment

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

cool

"""
skushnir123 marked this conversation as resolved.
Show resolved Hide resolved
One-hot encode a binary integer into a target register.

Registers:
a: an unsigned integer
b: the target to one-hot encode.

References:
[Windowed quantum arithmetic](https://arxiv.org/pdf/1905.07682.pdf)
skushnir123 marked this conversation as resolved.
Show resolved Hide resolved
Figure 4
"""

binary_bitsize: int
skushnir123 marked this conversation as resolved.
Show resolved Hide resolved

@property
def signature(self) -> 'Signature':
return Signature.build_from_dtypes(
a=QUInt(self.binary_bitsize), b=QAny(2**self.binary_bitsize)
)

def decompose_from_registers(
self, *, context: cirq.DecompositionContext, **quregs: NDArray[cirq.Qid]
) -> cirq.OP_TREE:
a = quregs['a'][::-1]
b = quregs['b']

yield cirq.X(b[0])
for i in range(len(a)):
for j in range(2**i):
yield TwoBitCSwap().on_registers(ctrl=a[i], x=b[j], y=b[2**i + j])
58 changes: 58 additions & 0 deletions qualtran/bloqs/data_loading/one_hot_encoding_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Copyright 2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import attrs
import cirq
import pytest
from attr import field
from numpy._typing import NDArray

from qualtran import GateWithRegisters, QUInt, Signature
from qualtran.bloqs.data_loading.one_hot_encoding import OneHotEncoding
from qualtran.cirq_interop.bit_tools import iter_bits
from qualtran.cirq_interop.testing import assert_circuit_inp_out_cirqsim


@attrs.frozen
class OneHotEncodingTest(GateWithRegisters):
integer: int = field()
size: int = field()

@property
def signature(self) -> 'Signature':
return Signature.build_from_dtypes(a=QUInt(self.size), b=QUInt(2**self.size))

skushnir123 marked this conversation as resolved.
Show resolved Hide resolved
def decompose_from_registers(
self, *, context: cirq.DecompositionContext, **quregs: NDArray[cirq.Qid]
) -> cirq.OP_TREE:
a = quregs['a']
skushnir123 marked this conversation as resolved.
Show resolved Hide resolved
b = quregs['b']
binary_repr = list(iter_bits(self.integer, self.size))
for i in range(self.size):
if binary_repr[i] == 1:
yield cirq.X(a[i])
yield OneHotEncoding(binary_bitsize=self.size).on_registers(a=a, b=b)


@pytest.mark.parametrize('integer', list(range(8)))
fdmalone marked this conversation as resolved.
Show resolved Hide resolved
def test_one_hot_encoding(integer):
bitsize = 3
gate = OneHotEncodingTest(integer, bitsize)
qubits = cirq.LineQubit.range(bitsize + 2**bitsize)
op = gate.on_registers(a=qubits[:bitsize], b=qubits[bitsize:])
circuit0 = cirq.Circuit(op)
skushnir123 marked this conversation as resolved.
Show resolved Hide resolved
initial_state = [0] * (bitsize + 2**bitsize)
final_state = [0] * (bitsize + 2**bitsize)
final_state[:bitsize] = list(iter_bits(integer, bitsize))
final_state[bitsize + integer] = 1
assert_circuit_inp_out_cirqsim(circuit0, qubits, initial_state, final_state)
Loading