Quantum Sine and Cosine Transforms
The quantum Sine and Cosine transforms functions are the quantum analog for the discrete Sine and Cosine transforms. The unitary versions of the type I and type II transforms are defined as follows:
The open library includes four functions, following the implementation in Ref. [1]:
QCT and QST of type I
Function: qct_qst_type1
Arguments:
x
:QArray[QBit]
The x
quantum argument is the quantum state on which we apply the transforms, according to the following unitary on \(n\equiv\)x.len
qubits:
Example
import numpy as np
from classiq import *
from classiq.execution import (
ClassiqBackendPreferences,
ClassiqSimulatorBackendNames,
ExecutionPreferences,
)
NUM_QUBITS = 4
execution_preferences = ExecutionPreferences(
num_shots=1,
backend_preferences=ClassiqBackendPreferences(
backend_name=ClassiqSimulatorBackendNames.SIMULATOR_STATEVECTOR
),
)
np.random.seed(123)
cos_data = np.random.rand(2 ** (NUM_QUBITS - 1) + 1)
cos_data = cos_data / np.linalg.norm(cos_data)
sin_data = np.random.rand(2 ** (NUM_QUBITS - 1) - 1)
sin_data = sin_data / np.linalg.norm(sin_data)
combined_data = np.append(cos_data / np.sqrt(2), sin_data / np.sqrt(2))
@qfunc
def main(x: Output[QNum]):
prepare_amplitudes(combined_data.tolist(), 0.0, x)
qct_qst_type1(x)
qmod = create_model(main, execution_preferences=execution_preferences)
write_qmod(qmod, "qct_qst_type1", decimal_precision=15)
qprog = synthesize(qmod)
result = execute(qprog).result_value()
qct_data = np.zeros(2 ** (NUM_QUBITS - 1) + 1).astype(complex)
qst_data = np.zeros(2 ** (NUM_QUBITS - 1) - 1).astype(complex)
for sample in result.parsed_state_vector:
value = int(sample.state["x"])
if value < 2 ** (NUM_QUBITS - 1) + 1:
qct_data[value] += sample.amplitude
else:
qst_data[int(value - 2 ** (NUM_QUBITS - 1) - 1)] += sample.amplitude
def dct1(n):
dct = np.array(
[
[
np.cos(np.pi * j * k / (n - 1))
* (np.sqrt(1 / 2) if j == 0 or j == n - 1 else 1)
* (np.sqrt(1 / 2) if k == 0 or k == n - 1 else 1)
for j in range(n)
]
for k in range(n)
]
) / np.sqrt((n - 1) / 2)
return dct
def dst1(n):
dst = np.array(
[
[np.sin(np.pi * (j + 1) * (k + 1) / (n + 1)) for j in range(n)]
for k in range(n)
]
) / np.sqrt((n + 1) / 2)
return dst
global_phase = np.exp(1j * np.angle(qct_data[0]))
measured_cos_res = np.real(qct_data / global_phase)
expected_cos_res = (dct1(2 ** (NUM_QUBITS - 1) + 1) @ cos_data) / np.sqrt(2)
print("measured result:", measured_cos_res)
print("expected result:", expected_cos_res)
assert np.allclose(measured_cos_res, expected_cos_res, atol=0.01)
measured result: [ 0.64936034 -0.13662084 0.02159456 0.08089956 0.06722088 0.18669631
0.02254746 -0.01198615 0.1123771 ]
expected result: [ 0.64936034 -0.13662084 0.02159456 0.08089956 0.06722088 0.18669631
0.02254746 -0.01198615 0.1123771 ]
global_phase = np.exp(1j * np.angle(qst_data[0]))
measured_sin_res = np.real(qst_data / global_phase)
expected_sin_res = (dst1(2 ** (NUM_QUBITS - 1) - 1) @ sin_data) / np.sqrt(2)
print("measured result:", measured_sin_res)
print("expected result:", expected_sin_res)
assert np.allclose(measured_sin_res, expected_sin_res, atol=0.01)
measured result: [ 0.57556991 0.04712138 0.22433696 -0.27513447 0.17796799 0.07685908
0.05378552]
expected result: [ 0.57556991 0.04712138 0.22433696 -0.27513447 0.17796799 0.07685908
0.05378552]
QCT and QST of type II
Function: qct_qst_type2
Arguments:
-
x
:QArray[QBit]
, -
q
:QBit
The x
quantum argument is the quantum state on which we apply the transforms, whereas the single q
qubit indicates the block, according to the following unitary on \(n+1\equiv\) x.len
\(+1\) qubits:
Function: qct_type2
Arguments:
x
:QArray[QBit]
: the quantum state on which we apply \({\rm DCT}^{(2)}\).
Function: qst_type2
Arguments:
x
:QArray[QBit]
: the quantum state on which we apply \({\rm DST}^{(2)}\).
Example
NUM_QUBITS = 4
cos_sin_data = np.random.rand(2 ** (NUM_QUBITS - 1))
cos_sin_data = cos_sin_data / np.linalg.norm(cos_sin_data)
@qfunc
def main(x: Output[QNum], q: Output[QBit]):
prepare_amplitudes(cos_sin_data.tolist(), 0.0, x)
allocate(1, q)
H(q)
qct_qst_type2(x, q)
qmod = create_model(main, execution_preferences=execution_preferences)
write_qmod(qmod, "qct_qst_type2", decimal_precision=15)
qprog = synthesize(qmod)
result = execute(qprog).result_value()
qct_data = np.zeros(2 ** (NUM_QUBITS - 1)).astype(complex)
qst_data = np.zeros(2 ** (NUM_QUBITS - 1)).astype(complex)
for sample in result.parsed_state_vector:
if sample.state["q"] == 0:
qct_data[int(sample.state["x"])] += sample.amplitude
else:
qst_data[int(sample.state["x"])] += sample.amplitude
def dct2(n):
dct = np.array(
[
[
np.cos(np.pi * j * (k + 1 / 2) / n) * (np.sqrt(1 / 2) if j == 0 else 1)
for j in range(n)
]
for k in range(n)
]
) / np.sqrt(n / 2)
return dct.T
def dst2(n):
dst = np.array(
[
[
np.sin(np.pi * (j + 1) * (k + 1 / 2) / n)
* (np.sqrt(1 / 2) if j == n - 1 else 1)
for j in range(n)
]
for k in range(n)
]
) / np.sqrt(n / 2)
return dst.T
global_phase = np.exp(1j * np.angle(qct_data[0]))
measured_cos_res = np.real(qct_data / global_phase)
expected_cos_res = (dct2(2 ** (NUM_QUBITS - 1)) @ cos_sin_data) / np.sqrt(2)
print("measured result:", measured_cos_res)
print("expected result:", expected_cos_res)
assert np.allclose(measured_cos_res, expected_cos_res, atol=0.01)
measured result: [ 0.65104654 -0.23305325 -0.11473439 0.02595719 -0.04930425 0.03323495
0.06553173 0.01252819]
expected result: [ 0.65104654 -0.23305325 -0.11473439 0.02595719 -0.04930425 0.03323495
0.06553173 0.01252819]
global_phase = np.exp(1j * np.angle(qst_data[0]))
measured_sin_res = np.real(qst_data / global_phase)
expected_sin_res = (dst2(2 ** (NUM_QUBITS - 1)) @ cos_sin_data) / np.sqrt(2)
print("measured result:", measured_sin_res)
print("expected result:", expected_sin_res)
assert np.allclose(measured_sin_res, expected_sin_res, atol=0.01)
measured result: [ 0.63981132 -0.2180174 0.13530848 -0.08552637 0.02796936 -0.03450769
0.12370004 -0.01455965]
expected result: [ 0.63981132 -0.2180174 0.13530848 -0.08552637 0.02796936 -0.03450769
0.12370004 -0.01455965]
@qfunc
def main(x: Output[QNum]):
prepare_amplitudes(cos_sin_data.tolist(), 0.0, x)
qct_type2(x)
qmod = create_model(main, execution_preferences=execution_preferences)
write_qmod(qmod, "qct_type2", decimal_precision=15)
qprog = synthesize(qmod)
result = execute(qprog).result_value()
qct_data = np.zeros(2 ** (NUM_QUBITS - 1)).astype(complex)
for sample in result.parsed_state_vector:
qct_data[int(sample.state["x"])] += sample.amplitude
global_phase = np.exp(1j * np.angle(qct_data[0]))
measured_cos_res = np.real(qct_data / global_phase)
expected_cos_res = dct2(2 ** (NUM_QUBITS - 1)) @ cos_sin_data
print("measured result:", measured_cos_res)
print("expected result:", expected_cos_res)
assert np.allclose(measured_cos_res, expected_cos_res, atol=0.01)
measured result: [ 0.92071884 -0.32958707 -0.16225893 0.03670901 -0.06972674 0.04700132
0.09267587 0.01771754]
expected result: [ 0.92071884 -0.32958707 -0.16225893 0.03670901 -0.06972674 0.04700132
0.09267587 0.01771754]
@qfunc
def main(x: Output[QNum]):
prepare_amplitudes(cos_sin_data.tolist(), 0.0, x)
qst_type2(x)
qmod = create_model(main, execution_preferences=execution_preferences)
write_qmod(qmod, "qst_type2", decimal_precision=15)
qprog = synthesize(qmod)
result = execute(qprog).result_value()
qst_data = np.zeros(2 ** (NUM_QUBITS - 1)).astype(complex)
for sample in result.parsed_state_vector:
qst_data[int(sample.state["x"])] += sample.amplitude
global_phase = np.exp(1j * np.angle(qst_data[0]))
measured_sin_res = np.real(qst_data / global_phase)
expected_sin_res = dst2(2 ** (NUM_QUBITS - 1)) @ cos_sin_data
print("measured result:", measured_sin_res)
print("expected result:", expected_sin_res)
assert np.allclose(measured_sin_res, expected_sin_res, atol=0.01)
measured result: [ 0.90482984 -0.30832317 0.19135509 -0.12095255 0.03955464 -0.04880124
0.17493827 -0.02059046]
expected result: [ 0.90482984 -0.30832317 0.19135509 -0.12095255 0.03955464 -0.04880124
0.17493827 -0.02059046]
References
[1]: Klappenecker, A., & Rotteler M., "Discrete Cosine Transforms on Quantum Computers".