diff --git a/qualtran/bloqs/data_loading/one-hot-note.ipynb b/qualtran/bloqs/data_loading/one-hot-note.ipynb new file mode 100644 index 000000000..2bb392061 --- /dev/null +++ b/qualtran/bloqs/data_loading/one-hot-note.ipynb @@ -0,0 +1,113 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": true + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/samuelkushnir/miniconda3/envs/Qualtran-New/lib/python3.10/site-packages/cotengra/hyperoptimizers/hyper.py:34: UserWarning: Couldn't import `kahypar` - skipping from default hyper optimizer and using basic `labels` method instead.\n", + " warnings.warn(\n" + ] + } + ], + "source": [ + "from qualtran.bloqs.data_loading.one_hot_encoding import OneHotEncoding\n", + "\n", + "bloq = OneHotEncoding(binary_bitsize=3)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "outputs": [ + { + "ename": "ValueError", + "evalue": "targets register must of shape (8, 8) but is of shape (1, 8)", + "output_type": "error", + "traceback": [ + "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m", + "\u001B[0;31mDecomposeNotImplementedError\u001B[0m Traceback (most recent call last)", + "File \u001B[0;32m~/Documents/GitHub/Qualtran/qualtran/_infra/gate_with_registers.py:291\u001B[0m, in \u001B[0;36mGateWithRegisters.decompose_bloq\u001B[0;34m(self)\u001B[0m\n\u001B[1;32m 290\u001B[0m \u001B[38;5;28;01mtry\u001B[39;00m:\n\u001B[0;32m--> 291\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[43mBloq\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mdecompose_bloq\u001B[49m\u001B[43m(\u001B[49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[43m)\u001B[49m\n\u001B[1;32m 292\u001B[0m \u001B[38;5;28;01mexcept\u001B[39;00m DecomposeNotImplementedError:\n", + "File \u001B[0;32m~/Documents/GitHub/Qualtran/qualtran/_infra/bloq.py:142\u001B[0m, in \u001B[0;36mBloq.decompose_bloq\u001B[0;34m(self)\u001B[0m\n\u001B[1;32m 129\u001B[0m \u001B[38;5;250m\u001B[39m\u001B[38;5;124;03m\"\"\"Decompose this Bloq into its constituent parts contained in a CompositeBloq.\u001B[39;00m\n\u001B[1;32m 130\u001B[0m \n\u001B[1;32m 131\u001B[0m \u001B[38;5;124;03mBloq users can call this function to delve into the definition of a Bloq. If you're\u001B[39;00m\n\u001B[0;32m (...)\u001B[0m\n\u001B[1;32m 140\u001B[0m \u001B[38;5;124;03m `build_composite_bloq` returns `NotImplemented`.\u001B[39;00m\n\u001B[1;32m 141\u001B[0m \u001B[38;5;124;03m\"\"\"\u001B[39;00m\n\u001B[0;32m--> 142\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[43m_decompose_from_build_composite_bloq\u001B[49m\u001B[43m(\u001B[49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[43m)\u001B[49m\n", + "File \u001B[0;32m~/Documents/GitHub/Qualtran/qualtran/_infra/bloq.py:51\u001B[0m, in \u001B[0;36m_decompose_from_build_composite_bloq\u001B[0;34m(bloq)\u001B[0m\n\u001B[1;32m 50\u001B[0m bb, initial_soqs \u001B[38;5;241m=\u001B[39m BloqBuilder\u001B[38;5;241m.\u001B[39mfrom_signature(bloq\u001B[38;5;241m.\u001B[39msignature, add_registers_allowed\u001B[38;5;241m=\u001B[39m\u001B[38;5;28;01mFalse\u001B[39;00m)\n\u001B[0;32m---> 51\u001B[0m out_soqs \u001B[38;5;241m=\u001B[39m \u001B[43mbloq\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mbuild_composite_bloq\u001B[49m\u001B[43m(\u001B[49m\u001B[43mbb\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mbb\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[43minitial_soqs\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 52\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m bb\u001B[38;5;241m.\u001B[39mfinalize(\u001B[38;5;241m*\u001B[39m\u001B[38;5;241m*\u001B[39mout_soqs)\n", + "File \u001B[0;32m~/Documents/GitHub/Qualtran/qualtran/_infra/bloq.py:126\u001B[0m, in \u001B[0;36mBloq.build_composite_bloq\u001B[0;34m(self, bb, **soqs)\u001B[0m\n\u001B[1;32m 112\u001B[0m \u001B[38;5;250m\u001B[39m\u001B[38;5;124;03m\"\"\"Override this method to define a Bloq in terms of its constituent parts.\u001B[39;00m\n\u001B[1;32m 113\u001B[0m \n\u001B[1;32m 114\u001B[0m \u001B[38;5;124;03mBloq authors should override this method. If you already have an instance of a `Bloq`,\u001B[39;00m\n\u001B[0;32m (...)\u001B[0m\n\u001B[1;32m 124\u001B[0m \u001B[38;5;124;03m `NotImplemented` if there is no decomposition.\u001B[39;00m\n\u001B[1;32m 125\u001B[0m \u001B[38;5;124;03m\"\"\"\u001B[39;00m\n\u001B[0;32m--> 126\u001B[0m \u001B[38;5;28;01mraise\u001B[39;00m DecomposeNotImplementedError(\u001B[38;5;124mf\u001B[39m\u001B[38;5;124m\"\u001B[39m\u001B[38;5;132;01m{\u001B[39;00m\u001B[38;5;28mself\u001B[39m\u001B[38;5;132;01m}\u001B[39;00m\u001B[38;5;124m does not declare a decomposition.\u001B[39m\u001B[38;5;124m\"\u001B[39m)\n", + "\u001B[0;31mDecomposeNotImplementedError\u001B[0m: OneHotEncoding(binary_bitsize=3) does not declare a decomposition.", + "\nDuring handling of the above exception, another exception occurred:\n", + "\u001B[0;31mValueError\u001B[0m Traceback (most recent call last)", + "Cell \u001B[0;32mIn[2], line 1\u001B[0m\n\u001B[0;32m----> 1\u001B[0m \u001B[38;5;28mlist\u001B[39m(\u001B[38;5;28mlist\u001B[39m(\u001B[43mbloq\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mdecompose_bloq\u001B[49m\u001B[43m(\u001B[49m\u001B[43m)\u001B[49m\u001B[38;5;241m.\u001B[39miter_bloqsoqs())[\u001B[38;5;241m3\u001B[39m][\u001B[38;5;241m0\u001B[39m]\u001B[38;5;241m.\u001B[39mbloq\u001B[38;5;241m.\u001B[39mdecompose_bloq()\u001B[38;5;241m.\u001B[39miter_bloqsoqs())[\u001B[38;5;241m0\u001B[39m][\u001B[38;5;241m0\u001B[39m]\n", + "File \u001B[0;32m~/Documents/GitHub/Qualtran/qualtran/_infra/gate_with_registers.py:293\u001B[0m, in \u001B[0;36mGateWithRegisters.decompose_bloq\u001B[0;34m(self)\u001B[0m\n\u001B[1;32m 291\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m Bloq\u001B[38;5;241m.\u001B[39mdecompose_bloq(\u001B[38;5;28mself\u001B[39m)\n\u001B[1;32m 292\u001B[0m \u001B[38;5;28;01mexcept\u001B[39;00m DecomposeNotImplementedError:\n\u001B[0;32m--> 293\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[43mdecompose_from_cirq_style_method\u001B[49m\u001B[43m(\u001B[49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[43m)\u001B[49m\n", + "File \u001B[0;32m~/Documents/GitHub/Qualtran/qualtran/cirq_interop/_cirq_to_bloq.py:600\u001B[0m, in \u001B[0;36mdecompose_from_cirq_style_method\u001B[0;34m(bloq, method_name)\u001B[0m\n\u001B[1;32m 598\u001B[0m context \u001B[38;5;241m=\u001B[39m cirq\u001B[38;5;241m.\u001B[39mDecompositionContext(qubit_manager\u001B[38;5;241m=\u001B[39mqm)\n\u001B[1;32m 599\u001B[0m dfr_method \u001B[38;5;241m=\u001B[39m \u001B[38;5;28mgetattr\u001B[39m(bloq, method_name)\n\u001B[0;32m--> 600\u001B[0m decomposed_optree \u001B[38;5;241m=\u001B[39m \u001B[43mdfr_method\u001B[49m\u001B[43m(\u001B[49m\u001B[43mcontext\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mcontext\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[43mall_quregs\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 601\u001B[0m \u001B[38;5;28;01mtry\u001B[39;00m:\n\u001B[1;32m 602\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m cirq_optree_to_cbloq(\n\u001B[1;32m 603\u001B[0m decomposed_optree, signature\u001B[38;5;241m=\u001B[39mbloq\u001B[38;5;241m.\u001B[39msignature, in_quregs\u001B[38;5;241m=\u001B[39min_quregs, out_quregs\u001B[38;5;241m=\u001B[39mout_quregs\n\u001B[1;32m 604\u001B[0m )\n", + "File \u001B[0;32m~/Documents/GitHub/Qualtran/qualtran/bloqs/data_loading/one_hot_encoding.py:80\u001B[0m, in \u001B[0;36mOneHotEncoding.decompose_from_registers\u001B[0;34m(self, context, **quregs)\u001B[0m\n\u001B[1;32m 78\u001B[0m op_tree: List[cirq\u001B[38;5;241m.\u001B[39mOperation] \u001B[38;5;241m=\u001B[39m []\n\u001B[1;32m 79\u001B[0m op_tree\u001B[38;5;241m.\u001B[39mappend(cirq\u001B[38;5;241m.\u001B[39mX(b[\u001B[38;5;241m0\u001B[39m][\u001B[38;5;241m0\u001B[39m]))\n\u001B[0;32m---> 80\u001B[0m op_tree\u001B[38;5;241m.\u001B[39mappend(\u001B[43mSwapWithZero\u001B[49m\u001B[43m(\u001B[49m\u001B[43m(\u001B[49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mbinary_bitsize\u001B[49m\u001B[43m,\u001B[49m\u001B[43m)\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;241;43m2\u001B[39;49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mbinary_bitsize\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43m(\u001B[49m\u001B[38;5;241;43m2\u001B[39;49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mbinary_bitsize\u001B[49m\u001B[43m,\u001B[49m\u001B[43m)\u001B[49m\u001B[43m)\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mon_registers\u001B[49m\u001B[43m(\u001B[49m\u001B[43mselection\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43ma\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mtargets\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mb\u001B[49m\u001B[43m)\u001B[49m)\n\u001B[1;32m 81\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m op_tree\n", + "File \u001B[0;32m~/Documents/GitHub/Qualtran/qualtran/_infra/gate_with_registers.py:364\u001B[0m, in \u001B[0;36mGateWithRegisters.on_registers\u001B[0;34m(self, **qubit_regs)\u001B[0m\n\u001B[1;32m 361\u001B[0m \u001B[38;5;28;01mdef\u001B[39;00m \u001B[38;5;21mon_registers\u001B[39m(\n\u001B[1;32m 362\u001B[0m \u001B[38;5;28mself\u001B[39m, \u001B[38;5;241m*\u001B[39m\u001B[38;5;241m*\u001B[39mqubit_regs: Union[cirq\u001B[38;5;241m.\u001B[39mQid, Sequence[cirq\u001B[38;5;241m.\u001B[39mQid], NDArray[cirq\u001B[38;5;241m.\u001B[39mQid]]\n\u001B[1;32m 363\u001B[0m ) \u001B[38;5;241m-\u001B[39m\u001B[38;5;241m>\u001B[39m cirq\u001B[38;5;241m.\u001B[39mOperation:\n\u001B[0;32m--> 364\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mon(\u001B[38;5;241m*\u001B[39m\u001B[43mmerge_qubits\u001B[49m\u001B[43m(\u001B[49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43msignature\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[43mqubit_regs\u001B[49m\u001B[43m)\u001B[49m)\n", + "File \u001B[0;32m~/Documents/GitHub/Qualtran/qualtran/_infra/gate_with_registers.py:82\u001B[0m, in \u001B[0;36mmerge_qubits\u001B[0;34m(registers, **qubit_regs)\u001B[0m\n\u001B[1;32m 80\u001B[0m full_shape \u001B[38;5;241m=\u001B[39m reg\u001B[38;5;241m.\u001B[39mshape \u001B[38;5;241m+\u001B[39m (reg\u001B[38;5;241m.\u001B[39mbitsize,)\n\u001B[1;32m 81\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m qubits\u001B[38;5;241m.\u001B[39mshape \u001B[38;5;241m!=\u001B[39m full_shape:\n\u001B[0;32m---> 82\u001B[0m \u001B[38;5;28;01mraise\u001B[39;00m \u001B[38;5;167;01mValueError\u001B[39;00m(\n\u001B[1;32m 83\u001B[0m \u001B[38;5;124mf\u001B[39m\u001B[38;5;124m'\u001B[39m\u001B[38;5;132;01m{\u001B[39;00mreg\u001B[38;5;241m.\u001B[39mname\u001B[38;5;132;01m}\u001B[39;00m\u001B[38;5;124m register must of shape \u001B[39m\u001B[38;5;132;01m{\u001B[39;00mfull_shape\u001B[38;5;132;01m}\u001B[39;00m\u001B[38;5;124m but is of shape \u001B[39m\u001B[38;5;132;01m{\u001B[39;00mqubits\u001B[38;5;241m.\u001B[39mshape\u001B[38;5;132;01m}\u001B[39;00m\u001B[38;5;124m'\u001B[39m\n\u001B[1;32m 84\u001B[0m )\n\u001B[1;32m 85\u001B[0m ret \u001B[38;5;241m+\u001B[39m\u001B[38;5;241m=\u001B[39m qubits\u001B[38;5;241m.\u001B[39mflatten()\u001B[38;5;241m.\u001B[39mtolist()\n\u001B[1;32m 86\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m ret\n", + "\u001B[0;31mValueError\u001B[0m: targets register must of shape (8, 8) but is of shape (1, 8)" + ] + } + ], + "source": [ + "list(list(bloq.decompose_bloq().iter_bloqsoqs())[3][0].bloq.decompose_bloq().iter_bloqsoqs())[0][0]" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 5, + "outputs": [ + { + "data": { + "text/plain": "
", + "image/png": "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" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from qualtran.drawing import show_bloq\n", + "show_bloq(list(bloq.decompose_bloq().iter_bloqsoqs())[3][0].bloq.decompose_bloq(), type=\"musical_score\")\n", + "# show_bloq(bloq.decompose_bloq(), type=\"musical_score\")" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [], + "metadata": { + "collapsed": false + } + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/qualtran/bloqs/data_loading/one_hot_encoding.py b/qualtran/bloqs/data_loading/one_hot_encoding.py new file mode 100644 index 000000000..fc71d6726 --- /dev/null +++ b/qualtran/bloqs/data_loading/one_hot_encoding.py @@ -0,0 +1,81 @@ +# 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. +from typing import Any, Dict, List + +import attrs +import cirq +import quimb.tensor as qtn +from numpy._typing import NDArray + +from qualtran import GateWithRegisters, QAny, QUInt, Register, Side, Signature, SoquetT +from qualtran.bloqs.basic_gates import TwoBitCSwap +from qualtran.bloqs.swap_network import SwapWithZero +from qualtran.cirq_interop._cirq_to_bloq import _add_my_tensors_from_gate +from qualtran.simulation.classical_sim import ClassicalValT + + +@attrs.frozen +class OneHotEncoding(GateWithRegisters): + """One-hot encode a binary integer into a target register. + + Args: + binary_bitsize: The number of bits in the binary integer register. There will be 2^binary_bitsize bits in the one-hot-encoded register. + + Registers: + a: an unsigned integer + b: the target to one-hot encode. + + References: + [Windowed quantum arithmetic](https://arxiv.org/abs/1905.07682) + Figure 4 + """ + + binary_bitsize: int + + @property + def signature(self) -> 'Signature': + return Signature( + [ + Register('a', QUInt(self.binary_bitsize), side=Side.THRU), + Register('b', QAny(2**self.binary_bitsize), side=Side.THRU), + ] + ) + + def add_my_tensors( + self, + tn: 'qtn.TensorNetwork', + tag: Any, + *, + incoming: Dict[str, 'SoquetT'], + outgoing: Dict[str, 'SoquetT'], + ): + _add_my_tensors_from_gate( + self, self.signature, self.pretty_name(), tn, tag, incoming=incoming, outgoing=outgoing + ) + + def on_classical_vals( + self, a: 'ClassicalValT', b: 'ClassicalValT' + ) -> Dict[str, 'ClassicalValT']: + return {'a': a, 'b': int(2**a)} + + def decompose_from_registers( + self, *, context: cirq.DecompositionContext, **quregs: NDArray[cirq.Qid] # type: ignore[type-var] + ) -> cirq.OP_TREE: + a = quregs['a'] + b = quregs['b'].reshape(1, len(quregs['b'])) + + op_tree: List[cirq.Operation] = [] + op_tree.append(cirq.X(b[0][0])) + op_tree.append(SwapWithZero((self.binary_bitsize,), 2**self.binary_bitsize, (1,)).on_registers(selection=a, targets=b)) + return op_tree diff --git a/qualtran/bloqs/data_loading/one_hot_encoding_test.py b/qualtran/bloqs/data_loading/one_hot_encoding_test.py new file mode 100644 index 000000000..f537f73a5 --- /dev/null +++ b/qualtran/bloqs/data_loading/one_hot_encoding_test.py @@ -0,0 +1,74 @@ +# 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. +from typing import List + +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)) + + def decompose_from_registers( + self, *, context: cirq.DecompositionContext, **quregs: NDArray[cirq.Qid] # type: ignore[type-var] + ) -> cirq.OP_TREE: + a = quregs['a'] + b = quregs['b'] + binary_repr = list(iter_bits(self.integer, self.size)) + op_tree: List[cirq.Operation] = [] + for i in range(self.size): + if binary_repr[i] == 1: + op_tree.append(cirq.X(a[i])) + op_tree.append(OneHotEncoding(binary_bitsize=self.size).on_registers(a=a, b=b)) + return op_tree + + +@pytest.mark.parametrize('integer', list(range(8))) +def test_one_hot_encoding(integer): + # Tests that the second register has a 1 in the 'integer' index and zeroes elsewhere. + # For example, if integer=4, then second register should a 1 in the 4th index and zeroes else. + bitsize = 3 + gate = OneHotEncodingTest(integer, bitsize) + print(gate.decompose_bloq()) + qubits = cirq.LineQubit.range(bitsize + 2**bitsize) + op = gate.on_registers(a=qubits[:bitsize], b=qubits[bitsize:]) + + # circuit0 = cirq.Circuit(op) + # 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) + + +@pytest.mark.parametrize('integer', list(range(8))) +def test_one_hot_encoding_classical(integer): + bitsize = 3 + gate = OneHotEncoding(bitsize) + vals = gate.call_classically(a=integer, b=0) + assert vals == (integer, 2**integer)