Пример #1
0
    def _variable_step_method_standard_tests(self, method):
        """tests to run on a variable step solver."""

        results = solve_ode(self.basic_rhs,
                            t_span=self.t_span,
                            y0=self.y0,
                            method=method,
                            atol=1e-10,
                            rtol=1e-10)

        expected = expm(-1j * np.pi * self.X.data)

        self.assertAllClose(results.y[-1], expected)

        # pylint: disable=unused-argument
        def quad_rhs(t, y):
            return Array([t**2], dtype=float)

        results = solve_ode(quad_rhs,
                            t_span=[0.0, 1.0],
                            y0=Array([0.0]),
                            method=method,
                            atol=1e-10,
                            rtol=1e-10)
        expected = Array([1.0 / 3])
        self.assertAllClose(results.y[-1], expected)
Пример #2
0
    def setUp(self):
        self.t_span = [0.0, 1.0]
        self.y0 = Array(np.eye(2, dtype=complex))

        self.X = Array([[0.0, 1.0], [1.0, 0.0]], dtype=complex)
        self.Y = Array([[0.0, -1j], [1j, 0.0]], dtype=complex)
        self.Z = Array([[1.0, 0.0], [0.0, -1.0]], dtype=complex)

        # simple generator and rhs
        # pylint: disable=unused-argument
        def generator(t):
            return -1j * 2 * np.pi * self.X / 2

        def rhs(t, y):
            return generator(t) @ y

        self.basic_generator = generator
        self.basic_rhs = rhs

        # define simple model
        self.w = 2.0
        self.r = 0.1
        signals = [Constant(self.w), Signal(lambda t: 1.0, self.w)]
        operators = [
            -1j * 2 * np.pi * self.Z / 2, -1j * 2 * np.pi * self.r * self.X / 2
        ]
        self.basic_model = GeneratorModel(operators=operators, signals=signals)
Пример #3
0
    def __init__(
        self,
        dt: float,
        samples: Union[Array, List],
        start_time: float = 0.0,
        duration: int = None,
        carrier_freq: float = 0.0,
        phase: float = 0.0,
        name: str = None,
    ):
        """Initialize a piecewise constant signal.

        Args:
            dt: The duration of each sample.
            samples: The array of samples.
            start_time: The time at which the signal starts.
            duration: The duration of the signal in samples.
            carrier_freq: The frequency of the carrier.
            phase: The phase of the carrier.
            name: name of the signal.
        """
        super().__init__(name)

        self._dt = dt

        if samples is not None:
            self._samples = Array(samples)
        else:
            self._samples = Array([0.0] * duration)

        self._start_time = start_time

        self._carrier_freq = Array(carrier_freq)
        self._phase = Array(phase)
Пример #4
0
def initial_state_converter(
        obj: Any,
        return_class: bool = False) -> Union[Array, Tuple[Array, Type]]:
    """Convert initial state object to an Array.

    Args:
        obj: An initial state.
        return_class: Optional. If True return the class to use
                      for converting the output y Array.

    Returns:
        Array: the converted initial state if ``return_class=False``.
        tuple: (Array, class) if ``return_class=True``.
    """
    # pylint: disable=invalid-name
    y0_cls = None
    if isinstance(obj, Array):
        y0, y0_cls = obj, None
    if isinstance(obj, QuantumState):
        y0, y0_cls = Array(obj.data), obj.__class__
    elif isinstance(obj, QuantumChannel):
        y0, y0_cls = Array(SuperOp(obj).data), SuperOp
    elif isinstance(obj, (BaseOperator, Gate, QuantumCircuit)):
        y0, y0_cls = Array(Operator(obj.data)), Operator
    else:
        y0, y0_cls = Array(obj), None
    if return_class:
        return y0, y0_cls
    return y0
Пример #5
0
    def __init__(
        self,
        envelope: Callable,
        carrier_freqs: Array,
        phases: Array,
        drift_array: Optional[Array] = None,
    ):
        """Initialize with vector-valued envelope, carrier frequencies for
        each entry, and a drift_array, which corresponds to the value of the
        signal when the "time-dependent terms" are "off".

        Args:
            envelope: function of a single float returning an array.
            carrier_freqs: list of carrier frequencies for each component of the
                           envelope.
            phases: list of carrier phases for each component of the envelope.
            drift_array: a default array meant to be the value of the envelope
                         when all "time-dependent terms" are off.
        """
        carrier_freqs = Array(carrier_freqs)
        phases = Array(phases)

        self.envelope = envelope
        self.carrier_freqs = carrier_freqs
        self.phases = phases

        self._im_angular_freqs = 1j * 2 * np.pi * carrier_freqs

        # if not supplied nothing is assumed, constant array is taken as all
        # zeros
        if drift_array is None:
            self.drift_array = Array(np.zeros(len(self.carrier_freqs)))
        else:
            self.drift_array = Array(drift_array)
Пример #6
0
    def test_transform_rhs_funcs_order_F(self):
        """Test rhs function conversion"""

        inner_spec = {"type": "array", "shape": (4,)}
        outer_spec = {"type": "array", "shape": (2, 2)}
        converter = StateTypeConverter(inner_spec, outer_spec)

        X = Array([[0.0, 1.0], [1.0, 0.0]])

        # do matrix multiplication (a truly '2d' operation)
        def rhs(t, y):
            return t * (y @ y)

        # pylint: disable=unused-argument
        def generator(t):
            return X

        new_rhs = converter.rhs_outer_to_inner(rhs)

        test_t = np.pi
        y_2d = Array([[1, 2], [3, 4]])
        y_1d = y_2d.flatten(order="F")

        expected_output = rhs(test_t, y_2d).flatten(order="F")
        output = new_rhs(test_t, y_1d)

        self.assertAllClose(output, expected_output)

        new_generator = converter.generator_outer_to_inner(generator)

        # verify generator vectorization
        expected_output = np.kron(Array(np.eye(2)), X)
        output = new_generator(test_t)

        self.assertAllClose(output, expected_output)
Пример #7
0
def vec_commutator(A: Array):
    r"""Linear algebraic vectorization of the linear map X -> [A, X]
    in column-stacking convention. In column-stacking convention we have

    .. math::
        vec(ABC) = C^T \otimes A vec(B),

    so for the commutator we have

    .. math::
        [A, \cdot] = A \cdot - \cdot A \mapsto id \otimes A - A^T \otimes id

    Note: this function is also "vectorized" in the programming sense.

    Args:
        A: Either a 2d array representing the matrix A described above,
           or a 3d array representing a list of matrices.

    Returns:
        Array: vectorized version of the map.
    """
    iden = Array(np.eye(A.shape[-1]))
    axes = list(range(A.ndim))
    axes[-1] = axes[-2]
    axes[-2] += 1
    return np.kron(iden, A) - np.kron(A.transpose(axes), iden)
Пример #8
0
def trim_t_results(
    results: OdeResult,
    t_span: Union[List, Tuple, Array],
    t_eval: Optional[Union[List, Tuple, Array]] = None,
) -> OdeResult:
    """Trim ``OdeResult`` object based on value of ``t_span`` and ``t_eval``.

    Args:
        results: Result object, assumed to contain solution at time points
                 from the output of ``validate_and_merge_t_span_t_eval(t_span, t_eval)``.
        t_span: Interval to solve over.
        t_eval: Time points to include in returned results.

    Returns:
        OdeResult: Results with only times/solutions in ``t_eval``. If ``t_eval``
                   is ``None``, does nothing, returning solver default output.
    """

    if t_eval is None:
        return results

    t_span = Array(t_span, backend="numpy")

    # remove endpoints if not included in t_eval
    if t_eval[0] != t_span[0]:
        results.t = results.t[1:]
        results.y = Array(results.y[1:])

    if t_eval[-1] != t_span[1]:
        results.t = results.t[:-1]
        results.y = Array(results.y[:-1])

    return results
Пример #9
0
    def test_diag_frame_operator_basic_hamiltonian(self):
        """Test setting a diagonal frame operator for the internally
        set up basic hamiltonian.
        """

        self._basic_frame_evaluate_test(Array([1.0, -1.0]), 1.123)
        self._basic_frame_evaluate_test(Array([1.0, -1.0]), np.pi)
Пример #10
0
def jax_odeint(
    rhs: Callable,
    t_span: Array,
    y0: Array,
    t_eval: Optional[Union[Tuple, List, Array]] = None,
    **kwargs,
):
    """Routine for calling `jax.experimental.ode.odeint`

    Args:
        rhs: Callable of the form :math:`f(t, y)`
        t_span: Interval to solve over.
        y0: Initial state.
        t_eval: Optional list of time points at which to return the solution.
        kwargs: Optional arguments to be passed to ``odeint``.

    Returns:
        OdeResult: Results object.
    """

    t_list = merge_t_args(t_span, t_eval)

    # determine direction of integration
    t_direction = np.sign(Array(t_list[-1] - t_list[0], backend="jax")).data

    results = odeint(
        lambda y, t: t_direction * rhs(t_direction * t, y),
        y0=y0,
        t=t_direction * t_list.data,
        **kwargs,
    )

    results = OdeResult(t=t_list, y=Array(results, backend="jax"))

    return trim_t_results(results, t_span, t_eval)
Пример #11
0
    def test_generator_out_of_frame(self):
        """Test operator_out_of_frame."""
        rng = np.random.default_rng(111)
        rand_op = rng.uniform(low=-10, high=10, size=(
            6, 6)) + 1j * rng.uniform(low=-10, high=10, size=(6, 6))

        frame_op = Array(rand_op - rand_op.conj().transpose())

        t = rng.uniform(low=-100, high=100)
        y0 = Array(
            rng.uniform(low=-10, high=10, size=(6, 6)) +
            1j * rng.uniform(low=-10, high=10, size=(6, 6)))

        self._test_generator_out_of_frame(t, frame_op, y0)
        self._test_generator_out_of_frame(t,
                                          frame_op,
                                          y0,
                                          y_in_frame_basis=True)
        self._test_generator_out_of_frame(t,
                                          frame_op,
                                          y0,
                                          return_in_frame_basis=True)
        self._test_generator_out_of_frame(t,
                                          frame_op,
                                          y0,
                                          y_in_frame_basis=True,
                                          return_in_frame_basis=True)
Пример #12
0
def to_array(op: Union[Operator, Array, List[Operator], List[Array]]):
    """Convert an operator or list of operators to an Array.

    Args:
        op: Either an Operator to be converted to an array, a list of Operators
            to be converted to a 3d array, or an array (which simply gets
            returned)
    Returns:
        Array: Array version of input
    """
    if op is None or isinstance(op, Array):
        return op

    if isinstance(op, list) and isinstance(op[0], Operator):
        shape = op[0].data.shape
        dtype = op[0].data.dtype
        arr = np.empty((len(op), *shape), dtype=dtype)
        for i, sub_op in enumerate(op):
            arr[i] = sub_op.data
        return Array(arr)

    if isinstance(op, Operator):
        return Array(op.data)

    return Array(op)
Пример #13
0
    def setUp(self):
        """Set up a basic parameterized simulation."""

        self.w = 5.0
        self.r = 0.1

        operators = [
            2 * np.pi * self.w * Array(Operator.from_label("Z").data) / 2,
            2 * np.pi * self.r * Array(Operator.from_label("X").data) / 2,
        ]

        ham = HamiltonianModel(operators=operators)

        self.ham = ham

        def param_sim(amp, drive_freq):
            signals = [
                Constant(1.0),
                Signal(lambda t: amp, carrier_freq=drive_freq)
            ]

            ham_copy = ham.copy()
            ham_copy.signals = signals

            results = solve_lmde(
                ham_copy,
                t_span=[0.0, 1 / self.r],
                y0=Array([0.0, 1.0], dtype=complex),
                method="jax_odeint",
                atol=1e-10,
                rtol=1e-10,
            )
            return results.y[-1]

        self.param_sim = param_sim
Пример #14
0
    def test_diag_frame_operator_basic_model(self):
        """Test setting a diagonal frame operator for the internally
        set up basic model.
        """

        self._basic_frame_evaluate_test(Array([1j, -1j]), 1.123)
        self._basic_frame_evaluate_test(Array([1j, -1j]), np.pi)
Пример #15
0
    def _apply(self, signal: BaseSignal) -> BaseSignal:
        """
        Applies a transformation on a signal, such as a convolution,
        low pass filter, etc. Once a convolution is applied the signal
        can longer have a carrier as the carrier is part of the signal
        value and gets convolved.

        Args:
            signal: A signal or list of signals to which the
                    transfer function will be applied.

        Returns:
            signal: The transformed signal or list of signals.

        Raises:
            QiskitError: if the signal is not pwc.
        """
        if isinstance(signal, PiecewiseConstant):
            # Perform a discrete time convolution.
            dt = signal.dt
            func_samples = Array([self._func(dt * i) for i in range(signal.duration)])
            func_samples = func_samples / sum(func_samples)
            sig_samples = Array([signal.value(dt * i) for i in range(signal.duration)])

            convoluted_samples = list(np.convolve(func_samples, sig_samples))

            return PiecewiseConstant(dt, convoluted_samples, carrier_freq=0.0, phase=0.0)
        else:
            raise QiskitError("Transfer function not defined on input.")
Пример #16
0
def vec_dissipator(L: Array):
    r"""Linear algebraic vectorization of the linear map
    X -> L X L^\dagger - 0.5 * (L^\dagger L X + X L^\dagger L)
    in column stacking convention.

    This gives

    .. math::
        \overline{L} \otimes L - 0.5(id \otimes L^\dagger L +
            (L^\dagger L)^T \otimes id)

    Note: this function is also "vectorized" in the programming sense.
    """
    iden = Array(np.eye(L.shape[-1]))
    axes = list(range(L.ndim))

    axes[-1] = axes[-2]
    axes[-2] += 1
    Lconj = L.conj()
    LdagL = Lconj.transpose(axes) @ L
    LdagLtrans = LdagL.transpose(axes)

    return np.kron(Lconj, iden) @ np.kron(iden, L) - 0.5 * (
        np.kron(iden, LdagL) + np.kron(LdagLtrans, iden)
    )
Пример #17
0
    def __init__(
        self,
        envelope: Union[Callable, complex, float, int],
        carrier_freq: float = 0.0,
        phase: float = 0.0,
        name: str = None,
    ):
        """
        Initializes a signal given by an envelop and an optional carrier.

        Args:
            envelope: Envelope function of the signal.
            carrier_freq: Frequency of the carrier.
            phase: The phase of the carrier.
            name: name of signal.
        """
        super().__init__(name)

        if isinstance(envelope, (float, int)):
            envelope = complex(envelope)

        if isinstance(envelope, complex):
            self.envelope = lambda t: envelope
        else:
            self.envelope = envelope

        self._carrier_freq = Array(carrier_freq)
        self._phase = Array(phase)
Пример #18
0
def fixed_step_solver_template(
    take_step: Callable,
    rhs_func: Callable,
    t_span: Array,
    y0: Array,
    max_dt: float,
    t_eval: Optional[Union[Tuple, List, Array]] = None,
):
    """Helper function for implementing fixed-step solvers supporting both
    ``t_span`` and ``max_dt`` arguments. ``take_step`` is assumed to be a
    function implementing a single step of size h of a fixed-step method.
    The signature of ``take_step`` is assumed to be:
        - rhs_func: Either a generator :math:`G(t)` or RHS function :math:`f(t,y)`.
        - t0: The current time.
        - y0: The current state.
        - h: The size of the step to take.

    It returns:
        - y: The state of the DE at time t0 + h.

    ``take_step`` is used to integrate the DE specified by ``rhs_func``
    through all points in ``t_eval``, taking steps no larger than ``max_dt``.
    Each interval in ``t_eval`` is divided into the least number of sub-intervals
    of equal length so that the sub-intervals are smaller than ``max_dt``.

    Args:
        take_step: Callable for fixed step integration.
        rhs_func: Callable, either a generator or rhs function.
        t_span: Interval to solve over.
        y0: Initial state.
        max_dt: Maximum step size.
        t_eval: Optional list of time points at which to return the solution.

    Returns:
        OdeResult: Results object.
    """

    # ensure the output of rhs_func is a raw array
    def wrapped_rhs_func(*args):
        return Array(rhs_func(*args)).data

    y0 = Array(y0).data

    t_list, h_list, n_steps_list = get_fixed_step_sizes(t_span, t_eval, max_dt)

    ys = [y0]
    for current_t, h, n_steps in zip(t_list, h_list, n_steps_list):
        y = ys[-1]
        inner_t = current_t
        for _ in range(n_steps):
            y = take_step(wrapped_rhs_func, inner_t, y, h)
            inner_t = inner_t + h
        ys.append(y)
    ys = Array(ys)

    results = OdeResult(t=t_list, y=ys)

    return trim_t_results(results, t_span, t_eval)
Пример #19
0
    def test_addition(self):
        """Tests the multiplication of signals."""

        # Test Constant
        const1 = Constant(0.3)
        const2 = Constant(0.5)
        self.assertTrue(isinstance(const1 + const2, Signal))
        self.assertEqual((const1 + const2).value(), 0.8)

        # Test Signal
        signal1 = Signal(3.0, carrier_freq=0.1)
        signal2 = Signal(lambda t: 2.0 * t ** 2, carrier_freq=0.1)
        self.assertTrue(isinstance(const1 + signal1, Signal))
        self.assertTrue(isinstance(signal1 + const1, Signal))
        self.assertTrue(isinstance(signal1 + signal2, Signal))
        self.assertEqual((signal1 + signal2).carrier_freq, 0.0)
        self.assertEqual((signal1 + const1).carrier_freq, 0.0)
        self.assertEqual((signal1 + signal2).envelope_value(), 3.0)
        expected = 21.0 * np.cos(0.1 * 2.0 * np.pi * 3.0)
        self.assertAlmostEqual((signal1 + signal2).envelope_value(3.0), expected, places=8)
        self.assertEqual((signal1 + signal2).value(), 3.0)
        self.assertEqual((signal1 + signal2).value(2.0), 11.0 * np.cos(0.1 * 2.0 * np.pi * 2.0))

        # Test piecewise constant
        dt = 1.0
        samples = Array([0.0, 0.0, 1.0, 2.0, 1.0, 0.0, 0.0])
        carrier_freq = 0.5
        pwc1 = PiecewiseConstant(dt=dt, samples=samples, carrier_freq=carrier_freq)

        dt = 1.0
        samples = Array([0.0, 0.0, 1.0, 2.0, 1.0, 0.0, 0.0])
        carrier_freq = 0.1
        pwc2 = PiecewiseConstant(dt=dt, samples=samples, carrier_freq=carrier_freq)

        # Test types
        self.assertTrue(isinstance(const1 + pwc1, Signal))
        self.assertTrue(isinstance(signal1 + pwc1, Signal))
        self.assertTrue(isinstance(pwc1 + pwc2, Signal))
        self.assertTrue(isinstance(pwc1 + const1, Signal))
        self.assertTrue(isinstance(pwc1 + signal1, Signal))

        # Test values
        self.assertEqual((pwc1 + pwc2).carrier_freq, 0.0)

        self.assertEqual((pwc1 + pwc2).envelope_value(), 0.0)
        expected = 1.0 * np.cos(0.5 * 2.0 * np.pi * 4.0) + 1.0 * np.cos(0.1 * 2.0 * np.pi * 4.0)
        self.assertAlmostEqual((pwc1 + pwc2).envelope_value(4.0), expected, places=8)
        self.assertEqual((pwc1 + pwc2).value(), 0.0)
        expected = 1.0 * np.cos(0.5 * 2.0 * np.pi * 4.0) + 1.0 * np.cos(0.1 * 2.0 * np.pi * 4.0)
        self.assertAlmostEqual((pwc1 + pwc2).value(4.0), expected, places=8)

        # Test phase
        pwc2 = PiecewiseConstant(dt=dt, samples=samples, carrier_freq=carrier_freq, phase=0.5)
        expected = 1.0 * np.cos(0.5 * 2.0 * np.pi * 4.0) + 1.0 * np.cos(
            0.1 * 2.0 * np.pi * 4.0 + 0.5
        )
        self.assertAlmostEqual((pwc1 + pwc2).envelope_value(4.0), expected, places=8)
Пример #20
0
    def __init__(
        self,
        frame_operator: Union[BaseFrame, Operator, Array],
        atol: float = 1e-10,
        rtol: float = 1e-10,
    ):
        """Initialize with a frame operator.

        Args:
            frame_operator: the frame operator, must be either
                            Hermitian or anti-Hermitian.
            atol: absolute tolerance when verifying that the frame_operator is
                  Hermitian or anti-Hermitian.
            rtol: relative tolerance when verifying that the frame_operator is
                  Hermitian or anti-Hermitian.
        """

        if issubclass(type(frame_operator), BaseFrame):
            frame_operator = frame_operator.frame_operator

        self._frame_operator = frame_operator
        frame_operator = to_array(frame_operator)

        if frame_operator is None:
            self._dim = None
            self._frame_diag = None
            self._frame_basis = None
            self._frame_basis_adjoint = None
        # if frame_operator is a 1d array, assume already diagonalized
        elif frame_operator.ndim == 1:

            # verify Hermitian or anti-Hermitian
            # if Hermitian convert to anti-Hermitian
            frame_operator = _is_herm_or_anti_herm(frame_operator,
                                                   atol=atol,
                                                   rtol=rtol)

            self._frame_diag = Array(frame_operator)
            self._frame_basis = Array(np.eye(len(frame_operator)))
            self._frame_basis_adjoint = self.frame_basis
            self._dim = len(self._frame_diag)
        # if not, diagonalize it
        else:

            # verify Hermitian or anti-Hermitian
            # if Hermitian convert to anti-Hermitian
            frame_operator = _is_herm_or_anti_herm(frame_operator,
                                                   atol=atol,
                                                   rtol=rtol)

            # diagonalize with eigh, utilizing assumption of anti-hermiticity
            frame_diag, frame_basis = np.linalg.eigh(1j * frame_operator)

            self._frame_diag = Array(-1j * frame_diag)
            self._frame_basis = Array(frame_basis)
            self._frame_basis_adjoint = frame_basis.conj().transpose()
            self._dim = len(self._frame_diag)
Пример #21
0
    def test_y0_reshape(self):
        """Test automatic detection of vectorized LMDE."""

        y0 = Array(np.eye(2))

        output = lmde_y0_reshape(4, y0)
        expected = y0.flatten(order="F")

        self.assertAllClose(output, expected)
Пример #22
0
    def from_outer_instance_inner_type_spec(cls, outer_y, inner_type_spec=None, order="F"):
        """Instantiate from concrete instance of the outer type,
        and an inner type-spec. The inner type spec can be either
        be fully specified, or be more general (i.e. to
        facilitate the situation in which a solver needs a 1d array).

        Accepted general data types:
            - {'type': 'array'}
            - {'type': 'array', 'ndim': 1}

        Args:
            outer_y (array): concrete outer data type
            inner_type_spec (dict): inner, potentially general, type spec
            order (str): order argument to be used in array reshaping.

        Returns:
            StateTypeConverter: type converter as specified by args

        Raises:
            Exception: if inner_type_spec is not properly specified or is
            not a handled type
        """

        # if no inner_type_spec given just instantiate both inner
        # and outer to the outer_y
        if inner_type_spec is None:
            return cls.from_instances(outer_y, order=order)

        inner_type = inner_type_spec.get("type")
        if inner_type is None:
            raise Exception("inner_type_spec needs a 'type' key.")

        if inner_type == "array":
            outer_y_as_array = Array(outer_y)

            # if a specific shape is given attempt to instantiate from a
            # reshaped outer_y
            shape = inner_type_spec.get("shape")
            if shape is not None:
                return cls.from_instances(
                    outer_y_as_array.reshape(shape, order=order), outer_y, order=order
                )

            # handle the case that ndim == 1 is given
            ndim = inner_type_spec.get("ndim")
            if ndim == 1:
                return cls.from_instances(
                    outer_y_as_array.flatten(order=order), outer_y, order=order
                )

            # if neither shape nor ndim is given, assume it can be an array
            # of any shape
            return cls.from_instances(outer_y_as_array, outer_y, order=order)

        raise Exception("inner_type_spec not a handled type.")
Пример #23
0
    def test_instantiation_errors(self):
        """Check different modes of error raising for frame setting."""

        with self.assertRaises(QiskitError):
            Frame(Array([1.0, 1j]))

        with self.assertRaises(QiskitError):
            Frame(Array([[1.0, 0.0], [0.0, 1j]]))

        with self.assertRaises(QiskitError):
            Frame(self.Z + 1j * self.X)
Пример #24
0
def merge_t_args(
    t_span: Union[List, Tuple, Array], t_eval: Optional[Union[List, Tuple, Array]] = None
) -> Array:
    """Merge ``t_span`` and ``t_eval`` into a single array without
    duplicates. Validity of the passed ``t_span`` and ``t_eval``
    follow scipy ``solve_ivp`` validation logic:
    ``t_eval`` must be contained in ``t_span``, and be strictly
    increasing if ``t_span[1] > t_span[0]`` or strictly
    decreasing if ``t_span[1] < t_span[0]``.

    Note: this is done explicitly with ``numpy``, and hence this is
    not differentiable or compilable using jax.

    Args:
        t_span: Interval to solve over.
        t_eval: Time points to include in returned results.

    Returns:
        Array: Combined list of times.

    Raises:
        ValueError: If one of several validation checks fail.
    """

    if t_eval is None:
        return Array(t_span)

    t_span = Array(t_span, backend="numpy")

    t_min = np.min(t_span)
    t_max = np.max(t_span)
    t_direction = np.sign(t_span[1] - t_span[0])

    t_eval = Array(t_eval, backend="numpy")

    if t_eval.ndim > 1:
        raise ValueError("t_eval must be 1 dimensional.")

    if np.min(t_eval) < t_min or np.max(t_eval) > t_max:
        raise ValueError("t_eval entries must lie in t_span.")

    diff = np.diff(t_eval)

    if np.any(t_direction * diff <= 0.0):
        raise ValueError("t_eval must be ordered according to the direction of integration.")

    # if endpoints are not included in t_span, add them
    if t_eval[0] != t_span[0]:
        t_eval = np.append(t_span[0], t_eval)

    if t_span[1] != t_eval[-1]:
        t_eval = np.append(t_eval, t_span[1])

    return Array(t_eval, backend="numpy")
Пример #25
0
    def test_type_spec_from_instance(self):
        """Test type_spec_from_instance"""

        y = Array([1, 2, 3, 4])
        type_spec = type_spec_from_instance(y)

        self.assertEqual(type_spec, {"type": "array", "shape": (4,)})

        y = Array([[1, 2], [3, 4], [5, 6]])
        type_spec = type_spec_from_instance(y)

        self.assertEqual(type_spec, {"type": "array", "shape": (3, 2)})
Пример #26
0
    def test_basic_lindblad_lmult(self):
        """Test lmult method of Lindblad generator OperatorModel."""
        A = Array([[1.0, 2.0], [3.0, 4.0]])

        t = 1.123
        ham = (2 * np.pi * self.w * self.Z.data / 2 + 2 * np.pi * self.r *
               np.cos(2 * np.pi * self.w * t) * self.X.data / 2)
        sm = Array([[0.0, 0.0], [1.0, 0.0]])

        expected = self._evaluate_lindblad_rhs(A, ham, [sm])
        value = self.basic_lindblad.lmult(t, A.flatten(order="F"))
        self.assertAllClose(expected, value.reshape(2, 2, order="F"))
Пример #27
0
    def test_multiplication(self):
        """Tests the multiplication of signals."""

        # Test Constant
        const1 = Constant(0.3)
        const2 = Constant(0.5)
        self.assertTrue(isinstance(const1 * const2, Signal))
        self.assertEqual((const1 * const2).value(), 0.15)
        self.assertEqual((const1 * const2).value(10.0), 0.15)

        # Test Signal
        signal1 = Signal(3.0, carrier_freq=0.1)
        signal2 = Signal(lambda t: 2.0 * t ** 2, carrier_freq=0.1)
        self.assertTrue(isinstance(const1 * signal1, Signal))
        self.assertTrue(isinstance(signal1 * const1, Signal))
        self.assertTrue(isinstance(signal1 * signal2, Signal))
        self.assertEqual((signal1 * signal2).carrier_freq, 0.2)
        self.assertEqual((signal1 * const1).carrier_freq, 0.1)
        self.assertEqual((signal1 * signal2).envelope_value(), 0.0)
        self.assertEqual((signal1 * signal2).envelope_value(3.0), 3.0 * 18.0)
        self.assertEqual((signal1 * signal2).value(), 0.0)
        self.assertEqual((signal1 * signal2).value(2.0), 24.0 * np.cos(0.2 * 2.0 * np.pi * 2.0))

        # Test piecewise constant
        dt = 1.0
        samples = Array([0.0, 0.0, 1.0, 2.0, 1.0, 0.0, 0.0])
        carrier_freq = 0.5
        pwc1 = PiecewiseConstant(dt=dt, samples=samples, carrier_freq=carrier_freq)

        dt = 2.0
        samples = Array([0.0, 0.0, 1.0, 2.0, 1.0, 0.0, 0.0])
        carrier_freq = 0.1
        pwc2 = PiecewiseConstant(dt=dt, samples=samples, carrier_freq=carrier_freq)

        # Test types
        self.assertTrue(isinstance(const1 * pwc1, Signal))
        self.assertTrue(isinstance(signal1 * pwc1, Signal))
        self.assertTrue(isinstance(pwc1 * pwc2, Signal))
        self.assertTrue(isinstance(pwc1 * const1, Signal))
        self.assertTrue(isinstance(pwc1 * signal1, Signal))

        # Test values
        self.assertEqual((pwc1 * pwc2).carrier_freq, 0.6)
        self.assertEqual((pwc1 * pwc2).envelope_value(), 0.0)
        self.assertEqual((pwc1 * pwc2).envelope_value(4.0), 1.0)
        self.assertEqual((pwc1 * pwc2).value(), 0.0)
        self.assertEqual((pwc1 * pwc2).value(4.0), 1.0 * np.cos(0.6 * 2.0 * np.pi * 4.0))

        # Test phase
        pwc2 = PiecewiseConstant(dt=dt, samples=samples, carrier_freq=carrier_freq, phase=0.5)
        self.assertEqual((pwc1 * pwc2).value(4.0), 1.0 * np.cos(0.6 * 2.0 * np.pi * 4.0 + 0.5))
Пример #28
0
    def test_convert_state_order_C(self):
        """Test convert_state with order parameter 'C'."""

        type_spec = {"type": "array", "shape": (4,)}
        y = Array([[1, 2], [3, 4]])
        expected = Array([1, 2, 3, 4])

        self.assertAllClose(convert_state(y, type_spec, order="C"), expected)

        type_spec = {"type": "array"}
        y = [[1, 2], [3, 4]]
        expected = Array([[1, 2], [3, 4]])

        self.assertAllClose(convert_state(y, type_spec, order="C"), expected)
Пример #29
0
    def setUp(self):
        self.X = Array(Operator.from_label("X").data)
        self.Y = Array(Operator.from_label("Y").data)
        self.Z = Array(Operator.from_label("Z").data)

        # define a basic hamiltonian
        w = 2.0
        r = 0.5
        operators = [2 * np.pi * self.Z / 2, 2 * np.pi * r * self.X / 2]
        signals = [Constant(w), Signal(1.0, w)]

        self.w = w
        self.r = r
        self.basic_hamiltonian = HamiltonianModel(operators=operators, signals=signals)
Пример #30
0
    def setUp(self):
        self.t_span = [0.0, 1.0]
        self.y0 = Array(np.eye(2, dtype=complex))

        self.X = Array([[0.0, 1.0], [1.0, 0.0]], dtype=complex)
        self.Y = Array([[0.0, -1j], [1j, 0.0]], dtype=complex)
        self.Z = Array([[1.0, 0.0], [0.0, -1.0]], dtype=complex)

        # simple generator and rhs
        # pylint: disable=unused-argument
        def generator(t):
            return -1j * 2 * np.pi * self.X / 2

        self.basic_generator = generator