Skip to content

Commit

Permalink
Merge branch 'main' into rg/zne-pulse-stretching
Browse files Browse the repository at this point in the history
  • Loading branch information
RolandMacDoland committed Dec 18, 2023
2 parents 3c635ed + 3d34caa commit a34c286
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 44 deletions.
35 changes: 18 additions & 17 deletions qadence/analog/parse_analog.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ def add_background_hamiltonian(
input_block,
_analog_to_hevo,
input_register,
(h_int, h_addr),
h_int,
h_addr,
)
else:
output_block = input_block
Expand All @@ -67,40 +68,40 @@ def add_background_hamiltonian(
return output_block


def _build_ham_evo(
block: WaitBlock | ConstantAnalogRotation,
def _build_rot_ham_evo(
block: ConstantAnalogRotation,
h_int: AbstractBlock,
h_drive: AbstractBlock | None,
h_addr: AbstractBlock | None,
h_drive: AbstractBlock,
) -> HamEvo:
duration = block.parameters.duration
h_block = h_int
if h_drive is not None:
h_block += h_drive
if block.add_pattern and h_addr is not None:
h_block += h_addr
return HamEvo(h_block, duration / 1000)
duration = block.parameters.duration
h_norm = block.parameters.h_norm
h_block += h_drive
return HamEvo(h_block / h_norm, duration * h_norm / 1000)


def _analog_to_hevo(
block: AbstractBlock,
register: Register,
h_terms: tuple[AbstractBlock, AbstractBlock | None],
block: AbstractBlock, register: Register, h_int: AbstractBlock, h_addr: AbstractBlock | None
) -> AbstractBlock:
"""
Converter from AnalogBlock to the respective HamEvo.
Any other block not covered by the specific conditions below is left unchanged.
"""

h_int, h_addr = h_terms

if isinstance(block, WaitBlock):
return _build_ham_evo(block, h_int, None, h_addr)
h_background = h_int
if block.add_pattern and h_addr is not None:
h_background += h_addr
duration = block.parameters.duration
return HamEvo(h_background, duration / 1000)

if isinstance(block, ConstantAnalogRotation):
h_drive = rydberg_drive_hamiltonian(block, register)
return _build_ham_evo(block, h_int, h_drive, h_addr)
return _build_rot_ham_evo(block, h_int, h_addr, h_drive)

if isinstance(block, AnalogKron):
# Needed to ensure kronned Analog blocks are implemented
Expand All @@ -112,9 +113,9 @@ def _analog_to_hevo(
for block in block.blocks:
if isinstance(block, ConstantAnalogRotation):
h_drive = rydberg_drive_hamiltonian(block, register)
ops.append(_build_ham_evo(block, h_int, h_drive, h_addr))
ops.append(_build_rot_ham_evo(block, h_int, h_addr, h_drive))
if len(ops) == 0:
ops.append(_build_ham_evo(block, h_int, None, h_addr)) # type: ignore [arg-type]
ops.append(HamEvo(h_background, duration / 1000)) # type: ignore [arg-type]
return chain(*ops)

return block
17 changes: 10 additions & 7 deletions qadence/backends/pulser/pulses.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,12 +147,12 @@ def add_pulses(
"backend. If an addressing pattern is specified, it will be added to all blocks."
)
ps = block.parameters
(t_uuid, duration) = ps.uuid_param("duration")
(a_uuid, alpha) = ps.uuid_param("alpha")
(w_uuid, omega) = ps.uuid_param("omega")
(p_uuid, phase) = ps.uuid_param("phase")
(d_uuid, detuning) = ps.uuid_param("delta")

t = evaluate(duration) if duration.is_number else sequence.declare_variable(t_uuid)
a = evaluate(alpha) if alpha.is_number else sequence.declare_variable(a_uuid)
w = evaluate(omega) if omega.is_number else sequence.declare_variable(w_uuid)
p = evaluate(phase) if phase.is_number else sequence.declare_variable(p_uuid)
d = evaluate(detuning) if detuning.is_number else sequence.declare_variable(d_uuid)
Expand All @@ -161,10 +161,10 @@ def add_pulses(
block.eigenvalues_generator = block.compute_eigenvalues_generator(block, qc_register)

if block.qubit_support.is_global:
pulse = analog_rot_pulse(t, w, p, d, global_channel, config)
pulse = analog_rot_pulse(a, w, p, d, global_channel, config)
sequence.add(pulse, GLOBAL_CHANNEL, protocol="wait-for-all")
else:
pulse = analog_rot_pulse(t, w, p, d, local_channel, config)
pulse = analog_rot_pulse(a, w, p, d, local_channel, config)
sequence.target(qubit_support, LOCAL_CHANNEL)
sequence.add(pulse, LOCAL_CHANNEL, protocol="wait-for-all")

Expand Down Expand Up @@ -197,7 +197,7 @@ def add_pulses(


def analog_rot_pulse(
duration: TVar | float,
alpha: TVar | float,
omega: TVar | float,
phase: TVar | float,
detuning: TVar | float,
Expand All @@ -215,17 +215,20 @@ def analog_rot_pulse(
max_amp = omega
max_det = detuning

# get pulse duration in ns
duration = 1000 * abs(alpha) / np.sqrt(omega**2 + detuning**2)

# create amplitude waveform
amp_wf = SquareWaveform.from_duration(
duration=abs(duration), # type: ignore
duration=duration, # type: ignore
max_amp=max_amp, # type: ignore[arg-type]
duration_steps=channel.clock_period, # type: ignore[attr-defined]
min_duration=channel.min_duration,
)

# create detuning waveform
det_wf = SquareWaveform.from_duration(
duration=abs(duration), # type: ignore
duration=duration, # type: ignore
max_amp=max_det, # type: ignore[arg-type]
duration_steps=channel.clock_period, # type: ignore[attr-defined]
min_duration=channel.min_duration,
Expand Down
26 changes: 20 additions & 6 deletions qadence/operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -1134,7 +1134,7 @@ def entangle(


def AnalogRot(
duration: float | str | Parameter = 1000.0,
duration: float | str | Parameter,
omega: float | str | Parameter = 0,
delta: float | str | Parameter = 0,
phase: float | str | Parameter = 0,
Expand All @@ -1153,13 +1153,20 @@ def AnalogRot(
Returns:
ConstantAnalogRotation
"""

if omega == 0 and delta == 0:
raise ValueError("Parameters omega and delta cannot both be 0.")

q = _cast(QubitSupport, qubit_support)
duration = Parameter(duration)
omega = Parameter(omega)
delta = Parameter(delta)
phase = Parameter(phase)
alpha = duration * sympy.sqrt(omega**2 + delta**2) / 1000
ps = ParamMap(alpha=alpha, duration=duration, omega=omega, delta=delta, phase=phase)
h_norm = sympy.sqrt(omega**2 + delta**2)
alpha = duration * h_norm / 1000
ps = ParamMap(
alpha=alpha, duration=duration, omega=omega, delta=delta, phase=phase, h_norm=h_norm
)
return ConstantAnalogRotation(parameters=ps, qubit_support=q, add_pattern=add_pattern)


Expand All @@ -1172,16 +1179,19 @@ def _analog_rot(
q = _cast(QubitSupport, qubit_support)
# assuming some arbitrary omega = π rad/μs
alpha = _cast(Parameter, angle)

delta = 0
omega = np.pi
duration = alpha / omega * 1000
h_norm = sympy.sqrt(omega**2 + delta**2)

# FIXME: once https://github.com/pasqal-io/qadence/issues/150 is fixed set default duration
# in the function arguments to:
# duration = Parameter(160)
# and compute omega like this:
# omega = alpha / duration * 1000
ps = ParamMap(alpha=alpha, duration=duration, omega=omega, delta=0, phase=phase)
ps = ParamMap(
alpha=alpha, duration=duration, omega=omega, delta=delta, phase=phase, h_norm=h_norm
)
return ConstantAnalogRotation(parameters=ps, qubit_support=q, add_pattern=add_pattern)


Expand Down Expand Up @@ -1246,8 +1256,12 @@ def AnalogRZ(
q = _cast(QubitSupport, qubit_support)
alpha = _cast(Parameter, angle)
delta = np.pi
omega = 0
duration = alpha / delta * 1000
ps = ParamMap(alpha=alpha, duration=duration, omega=0, delta=delta, phase=0.0)
h_norm = sympy.sqrt(omega**2 + delta**2)
ps = ParamMap(
alpha=alpha, duration=duration, omega=omega, delta=delta, phase=0.0, h_norm=h_norm
)
return ConstantAnalogRotation(qubit_support=q, parameters=ps, add_pattern=add_pattern)


Expand Down
15 changes: 9 additions & 6 deletions tests/backends/pulser_basic/test_differentiation.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from qadence.blocks import AbstractBlock, chain
from qadence.constructors import total_magnetization
from qadence.engines.torch.differentiable_backend import DifferentiableBackend
from qadence.operations import RX, RY, AnalogRot, AnalogRX, wait
from qadence.operations import RX, RY, AnalogRot, AnalogRX
from qadence.register import Register


Expand All @@ -20,24 +20,27 @@ def block(circ_id: int) -> AbstractBlock:

x = Parameter("x", trainable=False)

block: AbstractBlock

if circ_id == 1:
block = chain(RX(0, x), RY(1, x))
elif circ_id == 2:
block = chain(AnalogRot(duration=1000 * x / 3.0, omega=3.0))
block = AnalogRot(duration=1000 * x / 3.0, omega=3.0)
if circ_id == 3:
block = chain(AnalogRX(x))
block = AnalogRX(x)
elif circ_id == 4:
block = chain(
AnalogRX(np.pi / 2),
AnalogRot(duration=1000 * x / 3.0, omega=4.0, delta=3.0),
wait(500),
# FIXME: Re-check these tests after handling:
# https://github.com/pasqal-io/qadence/issues/266
# wait(500),
AnalogRX(np.pi / 2),
)

return block


@pytest.mark.slow
@pytest.mark.parametrize(
"block_id",
[1, 2, 3, 4],
Expand All @@ -56,7 +59,7 @@ def test_pulser_gpsr(block_id: int) -> None:
circ = QuantumCircuit(register, block(block_id))

# create input values
xs = torch.linspace(1, 2 * np.pi, 30, requires_grad=True)
xs = torch.linspace(1, 2 * np.pi, 5, requires_grad=True)
values = {"x": xs}

obs = total_magnetization(2)
Expand Down
11 changes: 3 additions & 8 deletions tests/backends/test_gpsr.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@
from qadence.blocks import add, chain
from qadence.constructors import total_magnetization
from qadence.engines.torch.differentiable_backend import DifferentiableBackend
from qadence.operations import CNOT, CRX, CRY, RX, RY, ConstantAnalogRotation, HamEvo, X, Y, Z
from qadence.parameters import ParamMap
from qadence.operations import CNOT, CRX, CRY, RX, RY, AnalogRot, HamEvo, X, Y, Z
from qadence.register import Register


Expand Down Expand Up @@ -126,12 +125,8 @@ def circuit_analog_rotation_gpsr(n_qubits: int) -> QuantumCircuit:
x = Parameter("x", trainable=False)
theta = Parameter("theta")
analog_block = chain(
ConstantAnalogRotation(
parameters=ParamMap(duration=1000 * x / omega1, omega=omega1, delta=0, phase=0)
),
ConstantAnalogRotation(
parameters=ParamMap(duration=1000 * theta / omega2, omega=omega2, delta=0, phase=0)
),
AnalogRot(duration=1000 * x / omega1, omega=omega1, delta=0, phase=0),
AnalogRot(duration=1000 * theta / omega2, omega=omega2, delta=0, phase=0),
)

circ = QuantumCircuit(register, analog_block)
Expand Down

0 comments on commit a34c286

Please sign in to comment.