コード例 #1
0
def sgd(loss, initial_parameters, args=(), options=None, compile=False):
    """Stochastic Gradient Descent (SGD) optimizer using Tensorflow backpropagation.

    See `tf.keras.Optimizers <https://www.tensorflow.org/api_docs/python/tf/keras/optimizers>`_
    for a list of the available optimizers.

    Args:
        loss (callable): Loss as a function of variational parameters to be
            optimized.
        initial_parameters (np.ndarray): Initial guess for the variational
            parameters.
        args (tuple): optional arguments for the loss function.
        options (dict): Dictionary with options for the SGD optimizer. Supports
            the following keys:

            - ``'optimizer'`` (str, default: ``'Adagrad'``): Name of optimizer.
            - ``'learning_rate'`` (float, default: ``'1e-3'``): Learning rate.
            - ``'nepochs'`` (int, default: ``1e6``): Number of epochs for optimization.
            - ``'nmessage'`` (int, default: ``1e3``): Every how many epochs to print
              a message of the loss function.
    """
    from qibo import K
    from qibo.config import log, raise_error
    if not K.supports_gradients:
        raise_error(RuntimeError, "SGD optimizer requires Tensorflow backend.")

    sgd_options = {
        "nepochs": 1000000,
        "nmessage": 1000,
        "optimizer": "Adagrad",
        "learning_rate": 0.001
    }
    if options is not None:
        sgd_options.update(options)

    # proceed with the training
    vparams = K.optimization.Variable(initial_parameters)
    optimizer = getattr(
        K.optimization.optimizers,
        sgd_options["optimizer"])(learning_rate=sgd_options["learning_rate"])

    def opt_step():
        with K.optimization.GradientTape() as tape:
            l = loss(vparams, *args)
        grads = tape.gradient(l, [vparams])
        optimizer.apply_gradients(zip(grads, [vparams]))
        return l

    if compile:
        loss = K.compile(loss)
        opt_step = K.compile(opt_step)

    for e in range(sgd_options["nepochs"]):
        l = opt_step()
        if e % sgd_options["nmessage"] == 1:
            log.info('ite %d : loss %f', e, l.numpy())

    return loss(vparams, *args).numpy(), vparams.numpy(), sgd_options
コード例 #2
0
def test_apply_twoqubit_gate_controlled(nqubits, targets, controls, compile,
                                        einsum_str):
    """Check ``K.op.apply_twoqubit_gate`` for random gates."""
    state = random_complex((2**nqubits, ))
    gate = random_complex((4, 4))
    gatenp = gate.numpy().reshape(4 * (2, ))

    target_state = state.numpy().reshape(nqubits * (2, ))
    slicer = nqubits * [slice(None)]
    for c in controls:
        slicer[c] = 1
    slicer = tuple(slicer)
    target_state[slicer] = np.einsum(einsum_str, target_state[slicer], gatenp)
    target_state = target_state.ravel()

    def apply_operator(state):
        qubits = qubits_tensor(nqubits, targets, controls)
        return K.op.apply_two_qubit_gate(state, gate, qubits, nqubits,
                                         *targets, get_threads())

    if compile:
        apply_operator = K.compile(apply_operator)

    state = apply_operator(state)
    np.testing.assert_allclose(target_state, state.numpy())
コード例 #3
0
def test_apply_zpow_gate(nqubits, target, controls, compile):
    """Check ``apply_zpow`` (including CZPow case)."""
    import itertools
    phase = np.exp(1j * 0.1234)
    qubits = controls[:]
    qubits.append(target)
    qubits.sort()
    matrix = np.ones(2**nqubits, dtype=np.complex128)
    for i, conf in enumerate(itertools.product([0, 1], repeat=nqubits)):
        if np.array(conf)[qubits].prod():
            matrix[i] = phase

    state = random_complex((2**nqubits, ))

    target_state = np.diag(matrix).dot(state.numpy())

    def apply_operator(state):
        qubits = qubits_tensor(nqubits, [target], controls)
        return K.op.apply_z_pow(state, phase, qubits, nqubits, target,
                                get_threads())

    if compile:
        apply_operator = K.compile(apply_operator)
    state = apply_operator(state)

    np.testing.assert_allclose(target_state, state.numpy())
コード例 #4
0
def test_apply_fsim(nqubits, targets, controls, compile, einsum_str):
    """Check ``K.op.apply_twoqubit_gate`` for random gates."""
    state = random_complex((2**nqubits, ))
    rotation = random_complex((2, 2))
    phase = random_complex((1, ))

    target_state = state.numpy().reshape(nqubits * (2, ))
    gatenp = np.eye(4, dtype=target_state.dtype)
    gatenp[1:3, 1:3] = rotation.numpy()
    gatenp[3, 3] = phase.numpy()[0]
    gatenp = gatenp.reshape(4 * (2, ))

    slicer = nqubits * [slice(None)]
    for c in controls:
        slicer[c] = 1
    slicer = tuple(slicer)
    target_state[slicer] = np.einsum(einsum_str, target_state[slicer], gatenp)
    target_state = target_state.ravel()

    gate = K.concatenate([K.reshape(rotation, (4, )), phase], axis=0)

    def apply_operator(state):
        qubits = qubits_tensor(nqubits, targets, controls)
        return K.op.apply_fsim(state, gate, qubits, nqubits, *targets,
                               get_threads())

    if compile:
        apply_operator = K.compile(apply_operator)

    state = apply_operator(state)
    np.testing.assert_allclose(target_state, state.numpy())
コード例 #5
0
def test_apply_swap_general(nqubits, targets, controls, compile):
    """Check ``apply_swap`` for more general cases."""
    state = random_complex((2**nqubits, ))

    target0, target1 = targets
    for q in controls:
        if q < targets[0]:
            target0 -= 1
        if q < targets[1]:
            target1 -= 1

    target_state = state.numpy().reshape(nqubits * (2, ))
    order = list(range(nqubits - len(controls)))
    order[target0], order[target1] = target1, target0
    slicer = tuple(1 if q in controls else slice(None) for q in range(nqubits))
    reduced_state = target_state[slicer]
    reduced_state = np.transpose(reduced_state, order)
    target_state[slicer] = reduced_state

    def apply_operator(state):
        qubits = qubits_tensor(nqubits, targets, controls)
        return K.op.apply_swap(state, qubits, nqubits, *targets, get_threads())

    if compile:
        apply_operator = K.compile(apply_operator)
    state = apply_operator(state)
    np.testing.assert_allclose(target_state.ravel(), state.numpy())
コード例 #6
0
def test_circuit_vs_gate_execution(backend, compile):
    """Check consistency between executing circuit and stand alone gates."""
    from qibo import K
    theta = 0.1234
    target_c = Circuit(2)
    target_c.add(gates.X(0))
    target_c.add(gates.X(1))
    target_c.add(gates.CU1(0, 1, theta))
    target_result = target_c()

    # custom circuit
    def custom_circuit(initial_state, theta):
        l1 = gates.X(0)(initial_state)
        l2 = gates.X(1)(l1)
        o = gates.CU1(0, 1, theta)(l2)
        return o

    initial_state = target_c.get_initial_state()
    if compile:
        c = K.compile(custom_circuit)
    else:
        c = custom_circuit

    result = c(initial_state, theta)
    K.assert_allclose(result, target_result)
コード例 #7
0
def test_circuit_vs_gate_execution(backend, compile):
    """Check consistency between executing circuit and stand alone gates."""
    from qibo import K
    original_backend = qibo.get_backend()
    qibo.set_backend(backend)
    theta = 0.1234
    target_c = Circuit(2)
    target_c.add(gates.X(0))
    target_c.add(gates.X(1))
    target_c.add(gates.CU1(0, 1, theta))
    target_result = target_c()

    # custom circuit
    def custom_circuit(initial_state, theta):
        l1 = gates.X(0)(initial_state)
        l2 = gates.X(1)(l1)
        o = gates.CU1(0, 1, theta)(l2)
        return o

    initial_state = target_c.get_initial_state()
    if compile:
        c = K.compile(custom_circuit)
    else:
        c = custom_circuit

    if backend == "custom" and compile:
        with pytest.raises(NotImplementedError):
            result = c(initial_state, theta)
    else:
        result = c(initial_state, theta)
        np.testing.assert_allclose(result, target_result)
    qibo.set_backend(original_backend)
コード例 #8
0
ファイル: variational.py プロジェクト: qiboteam/qibo
    def minimize(self, initial_state, method='Powell', jac=None, hess=None,
                 hessp=None, bounds=None, constraints=(), tol=None, callback=None,
                 options=None, compile=False, processes=None):
        """Search for parameters which minimizes the hamiltonian expectation.

        Args:
            initial_state (array): a initial guess for the parameters of the
                variational circuit.
            method (str): the desired minimization method.
                See :meth:`qibo.optimizers.optimize` for available optimization
                methods.
            jac (dict): Method for computing the gradient vector for scipy optimizers.
            hess (dict): Method for computing the hessian matrix for scipy optimizers.
            hessp (callable): Hessian of objective function times an arbitrary
                vector for scipy optimizers.
            bounds (sequence or Bounds): Bounds on variables for scipy optimizers.
            constraints (dict): Constraints definition for scipy optimizers.
            tol (float): Tolerance of termination for scipy optimizers.
            callback (callable): Called after each iteration for scipy optimizers.
            options (dict): a dictionary with options for the different optimizers.
            compile (bool): whether the TensorFlow graph should be compiled.
            processes (int): number of processes when using the paralle BFGS method.

        Return:
            The final expectation value.
            The corresponding best parameters.
            The optimization result object. For scipy methods it returns
            the ``OptimizeResult``, for ``'cma'`` the ``CMAEvolutionStrategy.result``,
            and for ``'sgd'`` the options used during the optimization.
        """
        def _loss(params, circuit, hamiltonian):
            circuit.set_parameters(params)
            final_state = circuit()
            return hamiltonian.expectation(final_state)

        if compile:
            if K.is_custom:
                raise_error(RuntimeError, "Cannot compile VQE that uses custom operators. "
                                          "Set the compile flag to False.")
            for gate in self.circuit.queue:
                _ = gate.cache
            loss = K.compile(_loss)
        else:
            loss = _loss

        if method == "cma":
            dtype = getattr(K.np, K._dtypes.get('DTYPE'))
            loss = lambda p, c, h: dtype(_loss(p, c, h))
        elif method != "sgd":
            loss = lambda p, c, h: K.to_numpy(_loss(p, c, h))

        result, parameters, extra = self.optimizers.optimize(loss, initial_state,
                                                             args=(self.circuit, self.hamiltonian),
                                                             method=method, jac=jac, hess=hess, hessp=hessp,
                                                             bounds=bounds, constraints=constraints,
                                                             tol=tol, callback=callback, options=options,
                                                             compile=compile, processes=processes)
        self.circuit.set_parameters(parameters)
        return result, parameters, extra
コード例 #9
0
 def compile(self):
     """Compiles the circuit as a Tensorflow graph."""
     if self._compiled_execute is not None:
         raise_error(RuntimeError, "Circuit is already compiled.")
     if not self.queue:
         raise_error(RuntimeError, "Cannot compile circuit without gates.")
     for gate in self.queue:
         # create gate cache before compilation
         _ = gate.cache
     self._compiled_execute = K.compile(self._execute_for_compile)
コード例 #10
0
 def compile(self):
     """Compiles the circuit as a Tensorflow graph."""
     if self._compiled_execute is not None:
         raise_error(RuntimeError, "Circuit is already compiled.")
     if not self.queue:
         raise_error(RuntimeError, "Cannot compile circuit without gates.")
     if K.custom_gates:
         raise_error(
             RuntimeError, "Cannot compile circuit that uses custom "
             "operators.")
     self._compiled_execute = K.compile(self._execute_for_compile)
コード例 #11
0
def test_initial_state_gradient(dtype, compile):  # pragma: no cover
    # Test skipped due to `tf.tensor_scatter_nd_update` bug on GPU
    def grad_default(var):
        update = np.array([1]).astype(dtype)
        with K.optimization.GradientTape() as tape:
            loss = K.backend.tensor_scatter_nd_update(var, [[0]], update)
        return tape.gradient(loss, var)

    def grad_custom(var):
        with K.optimization.GradientTape() as tape:
            loss = K.op.initial_state(var)
        return tape.gradient(loss, var)

    if compile:
        grad_default = K.compile(grad_default)
        grad_custom = K.compile(grad_custom)

    zeros = K.optimization.Variable(K.zeros(10, dtype=dtype))
    grad_reference = grad_default(zeros)
    grad_custom_op = grad_custom(zeros)
    np.testing.assert_allclose(grad_reference, grad_custom_op)
コード例 #12
0
def test_apply_swap_with_matrix(compile):
    """Check ``apply_swap`` for two qubits."""
    state = random_complex((2**2, ))
    matrix = np.array([[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]])
    target_state = matrix.dot(state.numpy())

    def apply_operator(state):
        qubits = qubits_tensor(2, [0, 1])
        return K.op.apply_swap(state, qubits, 2, 0, 1, get_threads())

    if compile:
        apply_operator = K.compile(apply_operator)
    state = apply_operator(state)
    np.testing.assert_allclose(target_state, state.numpy())
コード例 #13
0
def test_initial_state(dtype, compile):
    """Check that initial_state updates first element properly."""
    def apply_operator(dtype):
        """Apply the initial_state operator"""
        return K.op.initial_state(nqubits=4,
                                  dtype=dtype,
                                  is_matrix=False,
                                  omp_num_threads=get_threads())

    func = apply_operator
    if compile:
        func = K.compile(apply_operator)
    final_state = func(dtype)
    exact_state = np.array([1] + [0] * 15, dtype=dtype)
    np.testing.assert_allclose(final_state, exact_state)
コード例 #14
0
def test_apply_gate(nqubits, target, dtype, compile, einsum_str):
    """Check that ``K.op.apply_gate`` agrees with einsum gate implementation."""
    def apply_operator(state, gate):
        qubits = qubits_tensor(nqubits, [target])
        return K.op.apply_gate(state, gate, qubits, nqubits, target,
                               get_threads())

    state = random_complex((2**nqubits, ), dtype=dtype)
    gate = random_complex((2, 2), dtype=dtype)

    target_state = K.reshape(state, nqubits * (2, ))
    target_state = K.einsum(einsum_str, target_state, gate)
    target_state = target_state.numpy().ravel()

    if compile:
        apply_operator = K.compile(apply_operator)
    state = apply_operator(state, gate)
    np.testing.assert_allclose(target_state, state, atol=_atol)
コード例 #15
0
def test_apply_pauli_gate(nqubits, target, gate, compile):
    """Check ``apply_x``, ``apply_y`` and ``apply_z`` kernels."""
    matrices = {
        "x": np.array([[0, 1], [1, 0]], dtype=np.complex128),
        "y": np.array([[0, -1j], [1j, 0]], dtype=np.complex128),
        "z": np.array([[1, 0], [0, -1]], dtype=np.complex128)
    }
    state = random_complex((2**nqubits, ))
    target_state = K.cast(state, dtype=state.dtype)
    qubits = qubits_tensor(nqubits, [target])
    target_state = K.op.apply_gate(state, matrices[gate], qubits, nqubits,
                                   target, get_threads())

    def apply_operator(state):
        qubits = qubits_tensor(nqubits, [target])
        return getattr(K.op, "apply_{}".format(gate))(state, qubits, nqubits,
                                                      target, get_threads())

    if compile:
        apply_operator = K.compile(apply_operator)
    state = apply_operator(state)

    np.testing.assert_allclose(target_state.numpy(), state.numpy())
コード例 #16
0
def test_apply_gate_cx(nqubits, compile):
    """Check ``K.op.apply_gate`` for multiply-controlled X gates."""
    state = random_complex((2**nqubits, ))

    target_state = np.array(state)
    gate = np.eye(2**nqubits, dtype=target_state.dtype)
    gate[-2, -2], gate[-2, -1] = 0, 1
    gate[-1, -2], gate[-1, -1] = 1, 0
    target_state = np.dot(gate, target_state)

    xgate = K.cast([[0, 1], [1, 0]], dtype=state.dtype)
    controls = list(range(nqubits - 1))

    def apply_operator(state):
        qubits = qubits_tensor(nqubits, [nqubits - 1], controls)
        return K.op.apply_gate(state, xgate, qubits, nqubits, nqubits - 1,
                               get_threads())

    if compile:
        apply_operator = K.compile(apply_operator)
    state = apply_operator(state)

    np.testing.assert_allclose(target_state, state)
コード例 #17
0
def test_custom_op_toy_callback(gate, compile):
    """Check calculating ``callbacks`` using intermediate state values."""
    import functools
    state = random_complex((2**2, ))
    mask = random_complex((2**2, ))

    matrices = {
        "h": np.array([[1, 1], [1, -1]]) / np.sqrt(2),
        "x": np.array([[0, 1], [1, 0]]),
        "z": np.array([[1, 0], [0, -1]])
    }
    for k, v in matrices.items():
        matrices[k] = np.kron(v, np.eye(2))
    matrices["swap"] = np.array([[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0],
                                 [0, 0, 0, 1]])

    target_state = state.numpy()
    target_c1 = mask.numpy().dot(target_state)
    target_state = matrices[gate].dot(target_state)
    target_c2 = mask.numpy().dot(target_state)
    assert target_c1 != target_c2
    target_callback = [target_c1, target_c2]

    htf = K.cast(np.array([[1, 1], [1, -1]]) / np.sqrt(2), dtype=state.dtype)
    qubits_t1 = qubits_tensor(2, [0])
    qubits_t2 = qubits_tensor(2, [0, 1])
    apply_gate = {
        "h":
        functools.partial(K.op.apply_gate,
                          gate=htf,
                          qubits=qubits_t1,
                          nqubits=2,
                          target=0,
                          omp_num_threads=get_threads()),
        "x":
        functools.partial(K.op.apply_x,
                          qubits=qubits_t1,
                          nqubits=2,
                          target=0,
                          omp_num_threads=get_threads()),
        "z":
        functools.partial(K.op.apply_z,
                          qubits=qubits_t1,
                          nqubits=2,
                          target=0,
                          omp_num_threads=get_threads()),
        "swap":
        functools.partial(K.op.apply_swap,
                          qubits=qubits_t2,
                          nqubits=2,
                          target1=0,
                          target2=1,
                          omp_num_threads=get_threads())
    }

    def apply_operator(state):
        c1 = K.sum(mask * state)
        state0 = apply_gate[gate](state)
        c2 = K.sum(mask * state0)
        return state0, K.stack([c1, c2])

    if compile:  # pragma: no cover
        # case not tested because it fails
        apply_operator = K.compile(apply_operator)
    state, callback = apply_operator(state)

    np.testing.assert_allclose(target_state, state.numpy())
    np.testing.assert_allclose(target_callback, callback.numpy())