Signals (qiskit_dynamics.signals)#

This module contains classes for representing the time-dependent coefficients in matrix differential equations.

These classes, referred to as signals, represent classes of real-valued functions, either of the form, or built from functions of the following form:

\[s(t) = \textnormal{Re}[f(t)e^{i(2 \pi \nu t + \phi)}],\]

where

  • \(f\) is a complex-valued function called the envelope,

  • \(\nu \in \mathbb{R}\) is the carrier frequency, and

  • \(\phi \in \mathbb{R}\) is the phase.

Furthermore, this module contains transfer functions which transform one or more signal into other signals.

Signal API summary#

All signal classes share a common API for evaluation and visualization:

  • The signal value at a given time t is evaluated by treating the signal as a callable: signal(t).

  • The envelope \(f(t)\) is evaluated via: signal.envelope(t).

  • The complex value \(f(t)e^{i(2 \pi \nu t + \phi)}\) via: signal.complex_value(t).

  • The signal.draw method provides a common visualization interface.

In addition to the above, all signal types allow for algebraic operations, which should be understood in terms of algebraic operations on functions. E.g. two signals can be added together via

signal_sum = signal1 + signal2

and satisfy

signal_sum(t) == signal1(t) + signal2(t)

Signal multiplication is defined similarly, and signals can be added or multiplied with constants as well.

The remainder of this document gives further detail about some special functionality of these classes, but the following table provides a list of the different signal classes, along with a high level description of their role.

Table 4 Types of signal objects#

Class name

Description

Signal

Envelope specified as a python Callable, allowing for complete generality.

DiscreteSignal

Piecewise constant envelope, implemented with array-based operations, geared towards performance.

SignalSum

A sum of Signal or DiscreteSignal objects. Evaluation of envelopes returns an array of envelopes in the sum.

DiscreteSignalSum

A sum of DiscreteSignal objects with the same start time, number of samples, and sample duration. Implemented with array-based operations.

Constant Signal#

Signal supports specification of a constant signal:

const = Signal(2.)

This initializes the object to always return the constant 2., and allows constants to be treated on the same footing as arbitrary Signal instances. A Signal operating in constant-mode can be checked via the boolean attribute const.is_constant.

Algebraic operations#

Algebraic operations are supported by the SignalSum object. Any two signal classes can be added together, producing a SignalSum. Multiplication is also supported via SignalSum using the identity:

\[\begin{split}Re[f(t)e^{i(2 \pi \nu t + \phi)}] \times &Re[g(t)e^{i(2 \pi \omega t + \psi)}] \\&= Re[\frac{1}{2} f(t)g(t)e^{i(2\pi (\omega + \nu)t + (\phi + \psi))} ] + Re[\frac{1}{2} f(t)\overline{g(t)}e^{i(2\pi (\omega - \nu)t + (\phi - \psi))} ].\end{split}\]

I.e. multiplication of two base signals produces a SignalSum with two elements, whose envelopes, frequencies, and phases are as given by the above formula. Multiplication of sums is handled via distribution of this formula over the sum.

In the special case that DiscreteSignals with compatible sample structure (same number of samples, dt, and start time) are added together, a DiscreteSignalSum is produced. DiscreteSignalSum stores a sum of compatible DiscreteSignals by joining the underlying arrays, so that the sum can be evaluated using purely array-based operations. Multiplication of DiscreteSignals with compatible sample structure is handled similarly.

Sampling#

Both DiscreteSignal and DiscreteSignalSum feature constructors (from_Signal() and from_SignalSum() respectively) which build an instance by sampling a Signal or SignalSum. These constructors have the option to just sample the envelope (and keep the carrier analog), or to also sample the carrier. Below is a visualization of a signal superimposed with sampled versions, both in the case of sampling the carrier, and in the case of sampling just the envelope (and keeping the carrier analog).

../_images/signals_0_0.png

Transfer Functions#

A transfer function is a mapping from one or more Signal to one or more Signal. Transfer functions can, for example, be used to model the effect of the electronics finite response. The code below shows the example of an IQMixer. Here, two signals modulated at 100 MHz and with a relative \(\pi/2\) phase shift are passed through an IQ-mixer with a carrier frequency of 400 MHz to create a signal at 500 MHz. Note that the code below does not make any assumptions about the time and frequency units which we interpret as ns and GHz, respectively.

import numpy as np
from qiskit_dynamics.signals import DiscreteSignal, Sampler, IQMixer

dt = 0.25
in_phase = DiscreteSignal(dt, [1.0]*200, carrier_freq=0.1, phase=0)
quadrature = DiscreteSignal(dt, [1.0]*200, carrier_freq=0.1, phase=np.pi/2)

sampler = Sampler(dt/25, 5000)
in_phase = sampler(in_phase)
quadrature = sampler(quadrature)

mixer = IQMixer(0.4)
rf = mixer(in_phase, quadrature)

fig, axs = plt.subplots(1, 2, figsize=(14, 4))
in_phase.draw(0, 25, 100, axis=axs[0])
quadrature.draw(0, 25, 100, axis=axs[0], title='In-phase and quadrature signals')
rf.draw(0, 24, 2000, axis=axs[1], title='Mixer output')
../_images/signals_1_0.png

Signal Classes#

Signal(envelope[, carrier_freq, phase, name])

General signal class.

DiscreteSignal(dt, samples[, start_time, ...])

Piecewise constant signal implemented as an array of samples.

SignalSum(*signals[, name])

Represents a sum of signals.

DiscreteSignalSum(dt, samples[, start_time, ...])

Represents a sum of piecewise constant signals, all with the same time parameters: dt, number of samples, and start time.

SignalList(signal_list)

A list of signals with functionality for simultaneous evaluation.

Transfer Function Classes#

Convolution(func)

Applies a convolution as a sum

IQMixer(lo)

Implements an IQ Mixer.