Пример #1
0
    def __init__(self,
                 num_qubits: Optional[int] = None,
                 rotation_blocks: Optional[Union[QuantumCircuit, List[QuantumCircuit],
                                                 Instruction, List[Instruction]]] = None,
                 entanglement_blocks: Optional[Union[QuantumCircuit, List[QuantumCircuit],
                                                     Instruction, List[Instruction]]] = None,
                 entanglement: Optional[Union[List[int], List[List[int]]]] = None,
                 reps: int = 1,
                 insert_barriers: bool = False,
                 parameter_prefix: str = 'θ',
                 overwrite_block_parameters: Union[bool, List[List[Parameter]]] = True,
                 skip_final_rotation_layer: bool = False,
                 skip_unentangled_qubits: bool = False,
                 initial_state: Optional[Any] = None,
                 name: Optional[str] = 'nlocal') -> None:
        """Create a new n-local circuit.

        Args:
            num_qubits: The number of qubits of the circuit.
            rotation_blocks: The blocks used in the rotation layers. If multiple are passed,
                these will be applied one after another (like new sub-layers).
            entanglement_blocks: The blocks used in the entanglement layers. If multiple are passed,
                these will be applied one after another. To use different entanglements for
                the sub-layers, see :meth:`get_entangler_map`.
            entanglement: The indices specifying on which qubits the input blocks act. If None, the
                entanglement blocks are applied at the top of the circuit.
            reps: Specifies how often the rotation blocks and entanglement blocks are repeated.
            insert_barriers: If True, barriers are inserted in between each layer. If False,
                no barriers are inserted.
            parameter_prefix: The prefix used if default parameters are generated.
            overwrite_block_parameters: If the parameters in the added blocks should be overwritten.
                If False, the parameters in the blocks are not changed.
            skip_final_rotation_layer: Whether a final rotation layer is added to the circuit.
            skip_unentangled_qubits: If ``True``, the rotation gates act only on qubits that
                are entangled. If ``False``, the rotation gates act on all qubits.
            initial_state: A `qiskit.aqua.components.initial_states.InitialState` object which can
                be used to describe an initial state prepended to the NLocal circuit. This
                is primarily for compatibility with algorithms in Qiskit Aqua, which leverage
                this object to prepare input states.
            name: The name of the circuit.

        Examples:
            TODO

        Raises:
            ImportError: If an ``initial_state`` is specified but Qiskit Aqua is not installed.
            TypeError: If an ``initial_state`` is specified but not of the correct type,
                ``qiskit.aqua.components.initial_states.InitialState``.
            ValueError: If reps parameter is less than or equal to 0.
        """
        super().__init__(name=name)

        self._num_qubits = None
        self._insert_barriers = insert_barriers
        self._reps = reps
        self._entanglement_blocks = []
        self._rotation_blocks = []
        self._prepended_blocks = []
        self._prepended_entanglement = []
        self._appended_blocks = []
        self._appended_entanglement = []
        self._entanglement = None
        self._entangler_maps = None
        self._ordered_parameters = ParameterVector(name=parameter_prefix)
        self._overwrite_block_parameters = overwrite_block_parameters
        self._skip_final_rotation_layer = skip_final_rotation_layer
        self._skip_unentangled_qubits = skip_unentangled_qubits
        self._initial_state, self._initial_state_circuit = None, None
        self._data = None
        self._bounds = None

        if reps <= 0:
            raise ValueError('The value of reps should be larger than or equal to 1')

        if num_qubits is not None:
            self.num_qubits = num_qubits

        if entanglement_blocks is not None:
            self.entanglement_blocks = entanglement_blocks

        if rotation_blocks is not None:
            self.rotation_blocks = rotation_blocks

        if entanglement is not None:
            self.entanglement = entanglement

        if initial_state is not None:
            try:
                from qiskit.aqua.components.initial_states import InitialState
                if not isinstance(initial_state, InitialState):
                    raise TypeError('initial_state must be of type InitialState, but is '
                                    '{}.'.format(type(initial_state)))
            except ImportError as ex:
                raise ImportError(
                    'Could not import the qiskit.aqua.components.initial_states.'
                    'InitialState. To use this feature Qiskit Aqua must be installed.'
                ) from ex
            self.initial_state = initial_state
Пример #2
0
    def setUp(self):
        super().setUp()
        self.seed = 1376
        aqua_globals.random_seed = self.seed
        self.training_data = {
            'A': np.asarray([[2.95309709, 2.51327412],
                             [3.14159265, 4.08407045]]),
            'B': np.asarray([[4.08407045, 2.26194671],
                             [4.46106157, 2.38761042]])
        }
        self.testing_data = {
            'A': np.asarray([[3.83274304, 2.45044227]]),
            'B': np.asarray([[3.89557489, 0.31415927]])
        }

        self.ref_opt_params = np.array([
            10.03814083, -12.22048954, -7.58026833, -2.42392954, 12.91555293,
            13.44064652, -2.89951454, -10.20639406, 0.81414546, -1.00551752,
            -4.7988307, 14.00831419, 8.26008064, -7.07543736, 11.43368677,
            -5.74857438
        ])
        self.ref_train_loss = 0.69366523
        self.ref_prediction_a_probs = [[0.79882812, 0.20117188]]
        self.ref_prediction_a_label = [0]

        # ignore warnings from creating VariationalForm and FeatureMap objects
        warnings.filterwarnings('ignore', category=DeprecationWarning)
        var_form_ryrz = RYRZ(2, depth=3)
        feature_map = SecondOrderExpansion(2, depth=2)
        warnings.filterwarnings('always', category=DeprecationWarning)

        library_ryrz = TwoLocal(2, ['ry', 'rz'],
                                'cz',
                                reps=3,
                                insert_barriers=True)
        theta = ParameterVector('theta', var_form_ryrz.num_parameters)
        circuit_ryrz = var_form_ryrz.construct_circuit(theta)
        resorted = []
        for i in range(4):
            layer = library_ryrz.ordered_parameters[4 * i:4 * (i + 1)]
            resorted += layer[::2]
            resorted += layer[1::2]
        library_ryrz.assign_parameters(dict(zip(resorted, theta)),
                                       inplace=True)
        self._sorted_wavefunction_params = list(theta)

        self.ryrz_wavefunction = {
            'wrapped': var_form_ryrz,
            'circuit': circuit_ryrz,
            'library': library_ryrz
        }

        library_circuit = ZZFeatureMap(2, reps=2)
        x = ParameterVector('x', 2)
        circuit = feature_map.construct_circuit(x)
        self._sorted_data_params = list(x)
        library_circuit.assign_parameters(x, inplace=True)

        self.data_preparation = {
            'wrapped': feature_map,
            'circuit': circuit,
            'library': library_circuit
        }
Пример #3
0
class TestNLocal(QiskitTestCase):
    """Test the n-local circuit class."""

    def assertCircuitEqual(self, qc1, qc2, visual=False, transpiled=True):
        """An equality test specialized to circuits."""
        if transpiled:
            basis_gates = ['id', 'u1', 'u3', 'cx']
            qc1_transpiled = transpile(qc1, basis_gates=basis_gates, optimization_level=0)
            qc2_transpiled = transpile(qc2, basis_gates=basis_gates, optimization_level=0)
            qc1, qc2 = qc1_transpiled, qc2_transpiled

        if visual:
            self.assertEqual(qc1.draw(), qc2.draw())
        else:
            self.assertEqual(qc1, qc2)

    def test_empty_nlocal(self):
        """Test the creation of an empty NLocal."""
        nlocal = NLocal()
        self.assertEqual(nlocal.num_qubits, 0)
        self.assertEqual(nlocal.num_parameters_settable, 0)
        self.assertEqual(nlocal.reps, 1)

        self.assertEqual(nlocal, QuantumCircuit())

        for attribute in [nlocal.rotation_blocks, nlocal.entanglement_blocks]:
            self.assertEqual(len(attribute), 0)

    @data((XGate(), [[0], [2], [1]]),
          (XGate(), [[0]]),
          (CRXGate(-0.2), [[2, 0], [1, 3]]),
          )
    @unpack
    def test_add_layer_to_empty_nlocal(self, block, entangler_map):
        """Test appending gates to an empty nlocal."""
        nlocal = NLocal()
        nlocal.add_layer(block, entangler_map)

        max_num_qubits = max(max(indices) for indices in entangler_map)
        reference = QuantumCircuit(max_num_qubits + 1)
        for indices in entangler_map:
            reference.append(block, indices)

        self.assertCircuitEqual(nlocal, reference)

    @data([5, 3], [1, 5], [1, 1], [1, 2, 3, 10])
    def test_append_circuit(self, num_qubits):
        """Test appending circuits to an nlocal works normally."""
        # fixed depth of 3 gates per circuit
        depth = 3

        # keep track of a reference circuit
        reference = QuantumCircuit(max(num_qubits))

        # construct the NLocal from the first circuit
        first_circuit = random_circuit(num_qubits[0], depth)
        # TODO Terra bug: if this is to_gate it fails, since the QC adds an instruction not gate
        nlocal = NLocal(max(num_qubits), entanglement_blocks=first_circuit.to_instruction(), reps=1)
        reference.append(first_circuit, list(range(num_qubits[0])))

        # append the rest
        for num in num_qubits[1:]:
            circuit = random_circuit(num, depth)
            nlocal.append(circuit, list(range(num)))
            reference.append(circuit, list(range(num)))

        self.assertCircuitEqual(nlocal, reference)

    @data([5, 3], [1, 5], [1, 1], [1, 2, 3, 10])
    def test_add_nlocal(self, num_qubits):
        """Test adding an nlocal to an nlocal (using add_layer)."""
        # fixed depth of 3 gates per circuit
        depth = 3

        # keep track of a reference circuit
        reference = QuantumCircuit(max(num_qubits))

        # construct the NLocal from the first circuit
        first_circuit = random_circuit(num_qubits[0], depth)
        # TODO Terra bug: if this is to_gate it fails, since the QC adds an instruction not gate
        nlocal = NLocal(max(num_qubits), entanglement_blocks=first_circuit.to_instruction(), reps=1)
        reference.append(first_circuit, list(range(num_qubits[0])))

        # append the rest
        for num in num_qubits[1:]:
            circuit = random_circuit(num, depth)
            nlocal.add_layer(NLocal(num, entanglement_blocks=circuit, reps=1))
            reference.append(circuit, list(range(num)))

        self.assertCircuitEqual(nlocal, reference)

    @unittest.skip('Feature missing')
    def test_iadd_overload(self):
        """Test the overloaded + operator."""
        num_qubits, depth = 2, 2

        # construct two circuits for adding
        first_circuit = random_circuit(num_qubits, depth)
        circuit = random_circuit(num_qubits, depth)

        # get a reference
        reference = first_circuit + circuit

        # convert the object to be appended to different types
        others = [circuit, circuit.to_instruction(), circuit.to_gate(), NLocal(circuit)]

        # try adding each type
        for other in others:
            nlocal = NLocal(num_qubits, entanglement_blocks=first_circuit, reps=1)
            nlocal += other
            with self.subTest(msg='type: {}'.format(type(other))):
                self.assertCircuitEqual(nlocal, reference)

    def test_parameter_getter_from_automatic_repetition(self):
        """Test getting and setting of the nlocal parameters."""
        circuit = QuantumCircuit(2)
        circuit.ry(Parameter('a'), 0)
        circuit.crx(Parameter('b'), 0, 1)

        # repeat circuit and check that parameters are duplicated
        reps = 3
        nlocal = NLocal(2, entanglement_blocks=circuit, reps=reps)
        self.assertTrue(nlocal.num_parameters, 6)
        self.assertTrue(len(nlocal.parameters), 6)

    @data(list(range(6)), ParameterVector('θ', length=6), [0, 1, Parameter('theta'), 3, 4, 5])
    def test_parameter_setter_from_automatic_repetition(self, params):
        """Test getting and setting of the nlocal parameters."""
        circuit = QuantumCircuit(2)
        circuit.ry(Parameter('a'), 0)
        circuit.crx(Parameter('b'), 0, 1)

        # repeat circuit and check that parameters are duplicated
        reps = 3
        nlocal = NLocal(2, entanglement_blocks=circuit, reps=reps)
        nlocal.assign_parameters(params, inplace=True)

        param_set = set(p for p in params if isinstance(p, ParameterExpression))
        with self.subTest(msg='Test the parameters of the non-transpiled circuit'):
            # check the parameters of the final circuit
            self.assertEqual(nlocal.parameters, param_set)

        with self.subTest(msg='Test the parameters of the transpiled circuit'):
            basis_gates = ['id', 'u1', 'u2', 'u3', 'cx']
            transpiled_circuit = transpile(nlocal, basis_gates=basis_gates)
            self.assertEqual(transpiled_circuit.parameters, param_set)

    @data(list(range(6)), ParameterVector('θ', length=6), [0, 1, Parameter('theta'), 3, 4, 5])
    def test_parameters_setter(self, params):
        """Test setting the parameters via list."""
        # construct circuit with some parameters
        initial_params = ParameterVector('p', length=6)
        circuit = QuantumCircuit(1)
        for i, initial_param in enumerate(initial_params):
            circuit.ry(i * initial_param, 0)

        # create an NLocal from the circuit and set the new parameters
        nlocal = NLocal(1, entanglement_blocks=circuit, reps=1)
        nlocal.assign_parameters(params, inplace=True)

        param_set = set(p for p in params if isinstance(p, ParameterExpression))
        with self.subTest(msg='Test the parameters of the non-transpiled circuit'):
            # check the parameters of the final circuit
            self.assertEqual(nlocal.parameters, param_set)

        with self.subTest(msg='Test the parameters of the transpiled circuit'):
            basis_gates = ['id', 'u1', 'u2', 'u3', 'cx']
            transpiled_circuit = transpile(nlocal, basis_gates=basis_gates)
            self.assertEqual(transpiled_circuit.parameters, param_set)

    def test_repetetive_parameter_setting(self):
        """Test alternate setting of parameters and circuit construction."""
        x = Parameter('x')
        circuit = QuantumCircuit(1)
        circuit.rx(x, 0)

        nlocal = NLocal(1, entanglement_blocks=circuit, reps=3, insert_barriers=True)
        with self.subTest(msg='immediately after initialization'):
            self.assertEqual(len(nlocal.parameters), 3)

        with self.subTest(msg='after circuit construction'):
            self.assertEqual(len(nlocal.parameters), 3)

        q = Parameter('q')
        nlocal.assign_parameters([x, q, q], inplace=True)
        with self.subTest(msg='setting parameter to Parameter objects'):
            self.assertEqual(nlocal.parameters, set({x, q}))

        nlocal.assign_parameters([0, -1], inplace=True)
        with self.subTest(msg='setting parameter to numbers'):
            self.assertEqual(nlocal.parameters, set())

    def test_skip_unentangled_qubits(self):
        """Test skipping the unentangled qubits."""
        num_qubits = 6
        entanglement_1 = [[0, 1, 3], [1, 3, 5], [0, 1, 5]]
        skipped_1 = [2, 4]

        entanglement_2 = [
            entanglement_1,
            [[0, 1, 2], [2, 3, 5]]
        ]
        skipped_2 = [4]

        for entanglement, skipped in zip([entanglement_1, entanglement_2], [skipped_1, skipped_2]):
            with self.subTest(entanglement=entanglement, skipped=skipped):
                nlocal = NLocal(num_qubits, rotation_blocks=XGate(), entanglement_blocks=CCXGate(),
                                entanglement=entanglement, reps=3, skip_unentangled_qubits=True)

                skipped_set = set(nlocal.qubits[i] for i in skipped)
                dag = circuit_to_dag(nlocal)
                idle = set(dag.idle_wires())
                self.assertEqual(skipped_set, idle)

    @data('linear', 'full', 'circular', 'sca',
          ['linear', 'full'],
          ['circular', 'linear', 'sca'])
    def test_entanglement_by_str(self, entanglement):
        """Test setting the entanglement of the layers by str."""
        reps = 3
        nlocal = NLocal(5, rotation_blocks=XGate(), entanglement_blocks=CCXGate(),
                        entanglement=entanglement, reps=reps)

        def get_expected_entangler_map(rep_num, mode):
            if mode == 'linear':
                return [(0, 1, 2), (1, 2, 3), (2, 3, 4)]
            elif mode == 'full':
                return [(0, 1, 2), (0, 1, 3), (0, 1, 4), (0, 2, 3), (0, 2, 4), (0, 3, 4),
                        (1, 2, 3), (1, 2, 4), (1, 3, 4),
                        (2, 3, 4)]
            else:
                circular = [(3, 4, 0), (0, 1, 2), (1, 2, 3), (2, 3, 4)]
                if mode == 'circular':
                    return circular
                sca = circular[-rep_num:] + circular[:-rep_num]
                if rep_num % 2 == 1:
                    sca = [tuple(reversed(indices)) for indices in sca]
                return sca

        for rep_num in range(reps):
            entangler_map = nlocal.get_entangler_map(rep_num, 0, 3)
            if isinstance(entanglement, list):
                mode = entanglement[rep_num % len(entanglement)]
            else:
                mode = entanglement
            expected = get_expected_entangler_map(rep_num, mode)

            with self.subTest(rep_num=rep_num):
                # using a set here since the order does not matter
                self.assertEqual(set(entangler_map), set(expected))

    def test_entanglement_by_list(self):
        """Test setting the entanglement by list.

        This is the circuit we test (times 2, with final X layer)
                ┌───┐                ┌───┐┌───┐                  ┌───┐
        q_0: |0>┤ X ├──■────■───X────┤ X ├┤ X ├──■───X─────── .. ┤ X ├
                ├───┤  │    │   │    ├───┤└─┬─┘  │   │           ├───┤
        q_1: |0>┤ X ├──■────┼───┼──X─┤ X ├──■────┼───X──X──── .. ┤ X ├
                ├───┤┌─┴─┐  │   │  │ ├───┤  │    │      │     x2 ├───┤
        q_2: |0>┤ X ├┤ X ├──■───┼──X─┤ X ├──■────■──────X──X─ .. ┤ X ├
                ├───┤└───┘┌─┴─┐ │    ├───┤     ┌─┴─┐       │     ├───┤
        q_3: |0>┤ X ├─────┤ X ├─X────┤ X ├─────┤ X ├───────X─ .. ┤ X ├
                └───┘     └───┘      └───┘     └───┘             └───┘
        """
        circuit = QuantumCircuit(4)
        for _ in range(2):
            circuit.x([0, 1, 2, 3])
            circuit.barrier()
            circuit.ccx(0, 1, 2)
            circuit.ccx(0, 2, 3)
            circuit.swap(0, 3)
            circuit.swap(1, 2)
            circuit.barrier()
            circuit.x([0, 1, 2, 3])
            circuit.barrier()
            circuit.ccx(2, 1, 0)
            circuit.ccx(0, 2, 3)
            circuit.swap(0, 1)
            circuit.swap(1, 2)
            circuit.swap(2, 3)
            circuit.barrier()
        circuit.x([0, 1, 2, 3])

        layer_1_ccx = [(0, 1, 2), (0, 2, 3)]
        layer_1_swap = [(0, 3), (1, 2)]
        layer_1 = [layer_1_ccx, layer_1_swap]

        layer_2_ccx = [(2, 1, 0), (0, 2, 3)]
        layer_2_swap = [(0, 1), (1, 2), (2, 3)]
        layer_2 = [layer_2_ccx, layer_2_swap]

        entanglement = [layer_1, layer_2]

        nlocal = NLocal(4, rotation_blocks=XGate(), entanglement_blocks=[CCXGate(), SwapGate()],
                        reps=4, entanglement=entanglement, insert_barriers=True)

        self.assertCircuitEqual(nlocal, circuit)
Пример #4
0
class NLocal(BlueprintCircuit):
    """The n-local circuit class.

    The structure of the n-local circuit are alternating rotation and entanglement layers.
    In both layers, parameterized circuit-blocks act on the circuit in a defined way.
    In the rotation layer, the blocks are applied stacked on top of each other, while in the
    entanglement layer according to the ``entanglement`` strategy.
    The circuit blocks can have arbitrary sizes (smaller equal to the number of qubits in the
    circuit). Each layer is repeated ``reps`` times, and by default a final rotation layer is
    appended.

    For instance, a rotation block on 2 qubits and an entanglement block on 4 qubits using
    ``'linear'`` entanglement yields the following circuit.

    .. parsed-literal::

        ┌──────┐ ░ ┌──────┐                      ░ ┌──────┐
        ┤0     ├─░─┤0     ├──────────────── ... ─░─┤0     ├
        │  Rot │ ░ │      │┌──────┐              ░ │  Rot │
        ┤1     ├─░─┤1     ├┤0     ├──────── ... ─░─┤1     ├
        ├──────┤ ░ │  Ent ││      │┌──────┐      ░ ├──────┤
        ┤0     ├─░─┤2     ├┤1     ├┤0     ├ ... ─░─┤0     ├
        │  Rot │ ░ │      ││  Ent ││      │      ░ │  Rot │
        ┤1     ├─░─┤3     ├┤2     ├┤1     ├ ... ─░─┤1     ├
        ├──────┤ ░ └──────┘│      ││  Ent │      ░ ├──────┤
        ┤0     ├─░─────────┤3     ├┤2     ├ ... ─░─┤0     ├
        │  Rot │ ░         └──────┘│      │      ░ │  Rot │
        ┤1     ├─░─────────────────┤3     ├ ... ─░─┤1     ├
        └──────┘ ░                 └──────┘      ░ └──────┘

        |                                 |
        +---------------------------------+
               repeated reps times

    If specified, barriers can be inserted in between every block.
    If an initial state object of Qiskit Aqua is provided, it is added in front of the NLocal.
    """

    def __init__(self,
                 num_qubits: Optional[int] = None,
                 rotation_blocks: Optional[Union[QuantumCircuit, List[QuantumCircuit],
                                                 Instruction, List[Instruction]]] = None,
                 entanglement_blocks: Optional[Union[QuantumCircuit, List[QuantumCircuit],
                                                     Instruction, List[Instruction]]] = None,
                 entanglement: Optional[Union[List[int], List[List[int]]]] = None,
                 reps: int = 1,
                 insert_barriers: bool = False,
                 parameter_prefix: str = 'θ',
                 overwrite_block_parameters: Union[bool, List[List[Parameter]]] = True,
                 skip_final_rotation_layer: bool = False,
                 skip_unentangled_qubits: bool = False,
                 initial_state: Optional[Any] = None,
                 name: Optional[str] = 'nlocal') -> None:
        """Create a new n-local circuit.

        Args:
            num_qubits: The number of qubits of the circuit.
            rotation_blocks: The blocks used in the rotation layers. If multiple are passed,
                these will be applied one after another (like new sub-layers).
            entanglement_blocks: The blocks used in the entanglement layers. If multiple are passed,
                these will be applied one after another. To use different entanglements for
                the sub-layers, see :meth:`get_entangler_map`.
            entanglement: The indices specifying on which qubits the input blocks act. If None, the
                entanglement blocks are applied at the top of the circuit.
            reps: Specifies how often the rotation blocks and entanglement blocks are repeated.
            insert_barriers: If True, barriers are inserted in between each layer. If False,
                no barriers are inserted.
            parameter_prefix: The prefix used if default parameters are generated.
            overwrite_block_parameters: If the parameters in the added blocks should be overwritten.
                If False, the parameters in the blocks are not changed.
            skip_final_rotation_layer: Whether a final rotation layer is added to the circuit.
            skip_unentangled_qubits: If ``True``, the rotation gates act only on qubits that
                are entangled. If ``False``, the rotation gates act on all qubits.
            initial_state: A `qiskit.aqua.components.initial_states.InitialState` object which can
                be used to describe an initial state prepended to the NLocal circuit. This
                is primarily for compatibility with algorithms in Qiskit Aqua, which leverage
                this object to prepare input states.
            name: The name of the circuit.

        Examples:
            TODO

        Raises:
            ImportError: If an ``initial_state`` is specified but Qiskit Aqua is not installed.
            TypeError: If an ``initial_state`` is specified but not of the correct type,
                ``qiskit.aqua.components.initial_states.InitialState``.
            ValueError: If reps parameter is less than or equal to 0.
        """
        super().__init__(name=name)

        self._num_qubits = None
        self._insert_barriers = insert_barriers
        self._reps = reps
        self._entanglement_blocks = []
        self._rotation_blocks = []
        self._prepended_blocks = []
        self._prepended_entanglement = []
        self._appended_blocks = []
        self._appended_entanglement = []
        self._entanglement = None
        self._entangler_maps = None
        self._ordered_parameters = ParameterVector(name=parameter_prefix)
        self._overwrite_block_parameters = overwrite_block_parameters
        self._skip_final_rotation_layer = skip_final_rotation_layer
        self._skip_unentangled_qubits = skip_unentangled_qubits
        self._initial_state, self._initial_state_circuit = None, None
        self._data = None
        self._bounds = None

        if reps <= 0:
            raise ValueError('The value of reps should be larger than or equal to 1')

        if num_qubits is not None:
            self.num_qubits = num_qubits

        if entanglement_blocks is not None:
            self.entanglement_blocks = entanglement_blocks

        if rotation_blocks is not None:
            self.rotation_blocks = rotation_blocks

        if entanglement is not None:
            self.entanglement = entanglement

        if initial_state is not None:
            try:
                from qiskit.aqua.components.initial_states import InitialState
                if not isinstance(initial_state, InitialState):
                    raise TypeError('initial_state must be of type InitialState, but is '
                                    '{}.'.format(type(initial_state)))
            except ImportError as ex:
                raise ImportError(
                    'Could not import the qiskit.aqua.components.initial_states.'
                    'InitialState. To use this feature Qiskit Aqua must be installed.'
                ) from ex
            self.initial_state = initial_state

    @property
    def num_qubits(self) -> int:
        """Returns the number of qubits in this circuit.

        Returns:
            The number of qubits.
        """
        return self._num_qubits if self._num_qubits is not None else 0

    @num_qubits.setter
    def num_qubits(self, num_qubits: int) -> None:
        """Set the number of qubits for the n-local circuit.

        Args:
            The new number of qubits.
        """
        if self._num_qubits != num_qubits:
            # invalidate the circuit
            self._invalidate()
            self._num_qubits = num_qubits
            self.qregs = [QuantumRegister(num_qubits, name='q')]

    def _convert_to_block(self, layer: Any) -> QuantumCircuit:
        """Try to convert ``layer`` to a QuantumCircuit.

        Args:
            layer: The object to be converted to an NLocal block / Instruction.

        Returns:
            The layer converted to a circuit.

        Raises:
            TypeError: If the input cannot be converted to a circuit.
        """
        if isinstance(layer, QuantumCircuit):
            return layer

        if isinstance(layer, Instruction):
            circuit = QuantumCircuit(layer.num_qubits)
            circuit.append(layer, list(range(layer.num_qubits)))
            return circuit

        try:
            circuit = QuantumCircuit(layer.num_qubits)
            circuit.append(layer.to_instruction(), list(range(layer.num_qubits)))
            return circuit
        except AttributeError:
            pass

        raise TypeError('Adding a {} to an NLocal is not supported.'.format(type(layer)))

    @property
    def rotation_blocks(self) -> List[Instruction]:
        """The blocks in the rotation layers.

        Returns:
            The blocks in the rotation layers.
        """
        return self._rotation_blocks

    @rotation_blocks.setter
    def rotation_blocks(self, blocks: Union[QuantumCircuit, List[QuantumCircuit],
                                            Instruction, List[Instruction]]) -> None:
        """Set the blocks in the rotation layers.

        Args:
            blocks: The new blocks for the rotation layers.
        """
        # cannot check for the attribute ``'__len__'`` because a circuit also has this attribute
        if not isinstance(blocks, (list, numpy.ndarray)):
            blocks = [blocks]

        self._invalidate()
        self._rotation_blocks = [self._convert_to_block(block) for block in blocks]

    @property
    def entanglement_blocks(self) -> List[Instruction]:
        """The blocks in the entanglement layers.

        Returns:
            The blocks in the entanglement layers.
        """
        return self._entanglement_blocks

    @entanglement_blocks.setter
    def entanglement_blocks(self, blocks: Union[QuantumCircuit, List[QuantumCircuit],
                                                Instruction, List[Instruction]]) -> None:
        """Set the blocks in the entanglement layers.

        Args:
            blocks: The new blocks for the entanglement layers.
        """
        # cannot check for the attribute ``'__len__'`` because a circuit also has this attribute
        if not isinstance(blocks, (list, numpy.ndarray)):
            blocks = [blocks]

        self._invalidate()
        self._entanglement_blocks = [self._convert_to_block(block) for block in blocks]

    @property
    def entanglement(self) -> Union[str, List[str], List[List[str]], List[int], List[List[int]],
                                    List[List[List[int]]], List[List[List[List[int]]]],
                                    Callable[[int], str], Callable[[int], List[List[int]]]]:
        """Get the entanglement strategy.

        Returns:
            The entanglement strategy, see :meth:`get_entangler_map` for more detail on how the
            format is interpreted.
        """
        return self._entanglement

    @entanglement.setter
    def entanglement(self, entanglement: Optional[Union[str, List[str], List[List[str]], List[int],
                                                        List[List[int]], List[List[List[int]]],
                                                        List[List[List[List[int]]]],
                                                        Callable[[int], str],
                                                        Callable[[int], List[List[int]]]]]) -> None:
        """Set the entanglement strategy.

        Args:
            entanglement: The entanglement strategy. See :meth:`get_entangler_map` for more detail
                on the supported formats.
        """
        self._invalidate()
        self._entanglement = entanglement

    @property
    def num_layers(self) -> int:
        """Return the number of layers in the n-local circuit.

        Returns:
            The number of layers in the circuit.
        """
        return 2 * self._reps + int(not self._skip_final_rotation_layer)

    def _check_configuration(self, raise_on_failure: bool = True) -> bool:
        """Check if the configuration of the NLocal class is valid.

        Args:
            raise_on_failure: Whether to raise on failure.

        Returns:
            True, if the configuration is valid and the circuit can be constructed. Otherwise
            an ValueError is raised.

        Raises:
            ValueError: If the blocks are not set.
            ValueError: If the number of repetitions is not set.
            ValueError: If the qubit indices are not set.
            ValueError: If the number of qubit indices does not match the number of blocks.
            ValueError: If an index in the repetitions list exceeds the number of blocks.
            ValueError: If the number of repetitions does not match the number of block-wise
                parameters.
            ValueError: If a specified qubit index is larger than the (manually set) number of
                qubits.
        """
        valid = True
        if self.num_qubits is None:
            valid = False
            if raise_on_failure:
                raise ValueError('No number of qubits specified.')

        # check no needed parameters are None
        if self.entanglement_blocks is None and self.rotation_blocks is None:
            valid = False
            if raise_on_failure:
                raise ValueError('The blocks are not set.')

        return valid

    @property
    def ordered_parameters(self) -> List[Parameter]:
        """The parameters used in the underlying circuit.

        This includes float values and duplicates.

        Examples:

            >>> # prepare circuit ...
            >>> print(nlocal)
                 ┌───────┐┌──────────┐┌──────────┐┌──────────┐
            q_0: ┤ Ry(1) ├┤ Ry(θ[1]) ├┤ Ry(θ[1]) ├┤ Ry(θ[3]) ├
                 └───────┘└──────────┘└──────────┘└──────────┘
            >>> nlocal.parameters
            {Parameter(θ[1]), Parameter(θ[3])}
            >>> nlocal.ordered_parameters
            [1, Parameter(θ[1]), Parameter(θ[1]), Parameter(θ[3])]

        Returns:
            The parameters objects used in the circuit.
        """
        if isinstance(self._ordered_parameters, ParameterVector):
            self._ordered_parameters.resize(self.num_parameters_settable)
            return list(self._ordered_parameters)

        return self._ordered_parameters

    @ordered_parameters.setter
    def ordered_parameters(self, parameters: Union[ParameterVector, List[Parameter]]) -> None:
        """Set the parameters used in the underlying circuit.

        Args:
            The parameters to be used in the underlying circuit.

        Raises:
            ValueError: If the length of ordered parameters does not match the number of
                parameters in the circuit and they are not a ``ParameterVector`` (which could
                be resized to fit the number of parameters).
        """
        if not isinstance(parameters, ParameterVector) \
                and len(parameters) != self.num_parameters_settable:
            raise ValueError('The length of ordered parameters must be equal to the number of '
                             'settable parameters in the circuit ({}), but is {}'.format(
                                 self.num_parameters_settable, len(parameters)
                             ))
        self._ordered_parameters = parameters
        self._invalidate()

    @property
    def insert_barriers(self) -> bool:
        """If barriers are inserted in between the layers or not.

        Returns:
            True, if barriers are inserted in between the layers, False if not.
        """
        return self._insert_barriers

    @insert_barriers.setter
    def insert_barriers(self, insert_barriers: bool) -> None:
        """Specify whether barriers should be inserted in between the layers or not.

        Args:
            insert_barriers: If True, barriers are inserted, if False not.
        """
        # if insert_barriers changes, we have to invalidate the circuit definition,
        # if it is the same as before we can leave the NLocal instance as it is
        if insert_barriers is not self._insert_barriers:
            self._invalidate()
            self._insert_barriers = insert_barriers

    def get_unentangled_qubits(self) -> Set[int]:
        """Get the indices of unentangled qubits in a set.

        Returns:
            The unentangled qubits.
        """
        entangled_qubits = set()
        for i in range(self._reps):
            for j, block in enumerate(self.entanglement_blocks):
                entangler_map = self.get_entangler_map(i, j, block.num_qubits)
                entangled_qubits.update([idx for indices in entangler_map for idx in indices])
        unentangled_qubits = set(range(self.num_qubits)) - entangled_qubits

        return unentangled_qubits

    @property
    def num_parameters_settable(self) -> int:
        """The number of total parameters that can be set to distinct values.

        This does not change when the parameters are bound or exchanged for same parameters,
        and therefore is different from ``num_parameters`` which counts the number of unique
        :class:`~qiskit.circuit.Parameter` objects currently in the circuit.

        Returns:
            The number of parameters originally available in the circuit.

        Note:
            This quantity does not require the circuit to be built yet.
        """
        num = 0

        for i in range(self._reps):
            for j, block in enumerate(self.entanglement_blocks):
                entangler_map = self.get_entangler_map(i, j, block.num_qubits)
                num += len(entangler_map) * len(get_parameters(block))

        if self._skip_unentangled_qubits:
            unentangled_qubits = self.get_unentangled_qubits()

        num_rot = 0
        for block in self.rotation_blocks:
            block_indices = [
                list(range(j * block.num_qubits, (j + 1) * block.num_qubits))
                for j in range(self.num_qubits // block.num_qubits)
            ]
            if self._skip_unentangled_qubits:
                block_indices = [indices for indices in block_indices
                                 if set(indices).isdisjoint(unentangled_qubits)]
            num_rot += len(block_indices) * len(get_parameters(block))

        num += num_rot * (self._reps + int(not self._skip_final_rotation_layer))

        return num

    @property
    def parameters(self) -> Set[Parameter]:
        """Get the :class:`~qiskit.circuit.Parameter` objects in the circuit.

        Returns:
            A set containing the unbound circuit parameters.
        """
        self._build()
        return super().parameters

    @property
    def reps(self) -> int:
        """The number of times rotation and entanglement block are repeated.

        Returns:
            The number of repetitions.
        """
        return self._reps

    @reps.setter
    def reps(self, repetitions: int) -> None:
        """Set the repetitions.

        Args:
            repetitions: The new repetitions.

        Raises:
            ValueError: If reps setter has parameter repetitions <= 0.
        """
        if repetitions <= 0:
            raise ValueError('The repetitions should be larger than or equal to 1')
        if repetitions != self._reps:
            self._invalidate()
            self._reps = repetitions

    def print_settings(self) -> str:
        """Returns information about the setting.

        Returns:
            The class name and the attributes/parameters of the instance as ``str``.
        """
        ret = 'NLocal: {}\n'.format(self.__class__.__name__)
        params = ''
        for key, value in self.__dict__.items():
            if key[0] == '_':
                params += '-- {}: {}\n'.format(key[1:], value)
        ret += '{}'.format(params)
        return ret

    @property
    def preferred_init_points(self) -> Optional[List[float]]:
        """The initial points for the parameters. Can be stored as initial guess in optimization.

        Returns:
            The initial values for the parameters, or None, if none have been set.
        """
        return None

    # pylint: disable=too-many-return-statements
    def get_entangler_map(self, rep_num: int, block_num: int, num_block_qubits: int
                          ) -> List[List[int]]:
        """Get the entangler map for in the repetition ``rep_num`` and the block ``block_num``.

        The entangler map for the current block is derived from the value of ``self.entanglement``.
        Below the different cases are listed, where ``i`` and ``j`` denote the repetition number
        and the block number, respectively, and ``n`` the number of qubits in the block.

        entanglement type              | entangler map
        -------------------------------+--------------------------------------------------------
        None                           | [[0, ..., n - 1]]
        str (e.g 'full')               | the specified connectivity on ``n`` qubits
        List[int]                      | [``entanglement``]
        List[List[int]]                | ``entanglement``
        List[List[List[int]]]          | ``entanglement[i]``
        List[List[List[List[int]]]]    | ``entanglement[i][j]``
        List[str]                      | the connectivity specified in ``entanglement[i]``
        List[List[str]]                | the connectivity specified in ``entanglement[i][j]``
        Callable[int, str]             | same as List[str]
        Callable[int, List[List[int]]] | same as List[List[List[int]]]

        Note that all indices are to be taken modulo the length of the array they act on, i.e.
        no out-of-bounds index error will be raised but we re-iterate from the beginning of the
        list.

        Args:
            rep_num: The current repetition we are in.
            block_num: The block number within the entanglement layers.
            num_block_qubits: The number of qubits in the block.

        Returns:
            The entangler map for the current block in the current repetition.

        Raises:
            ValueError: If the value of ``entanglement`` could not be cast to a corresponding
                entangler map.
        """
        i, j, n = rep_num, block_num, num_block_qubits
        entanglement = self._entanglement

        # entanglement is None
        if entanglement is None:
            return [list(range(n))]

        # entanglement is callable
        if callable(entanglement):
            entanglement = entanglement(i)

        # entanglement is str
        if isinstance(entanglement, str):
            return get_entangler_map(n, self.num_qubits, entanglement, offset=i)

        # check if entanglement is list of something
        if not isinstance(entanglement, (tuple, list)):
            raise ValueError('Invalid value of entanglement: {}'.format(entanglement))
        num_i = len(entanglement)

        # entanglement is List[str]
        if all(isinstance(e, str) for e in entanglement):
            return get_entangler_map(n, self.num_qubits, entanglement[i % num_i], offset=i)

        # entanglement is List[int]
        if all(isinstance(e, int) for e in entanglement):
            return [entanglement]

        # check if entanglement is List[List]
        if not all(isinstance(e, (tuple, list)) for e in entanglement):
            raise ValueError('Invalid value of entanglement: {}'.format(entanglement))
        num_j = len(entanglement[i % num_i])

        # entanglement is List[List[str]]
        if all(isinstance(e2, str) for e in entanglement for e2 in e):
            return get_entangler_map(n, self.num_qubits, entanglement[i % num_i][j % num_j],
                                     offset=i)

        # entanglement is List[List[int]]
        if all(isinstance(e2, int) for e in entanglement for e2 in e):
            return entanglement

        # check if entanglement is List[List[List]]
        if not all(isinstance(e2, (tuple, list)) for e in entanglement for e2 in e):
            raise ValueError('Invalid value of entanglement: {}'.format(entanglement))

        # entanglement is List[List[List[int]]]
        if all(isinstance(e3, int) for e in entanglement for e2 in e for e3 in e2):
            return entanglement[i % num_i]

        # check if entanglement is List[List[List[List]]]
        if not all(isinstance(e3, (tuple, list)) for e in entanglement for e2 in e for e3 in e2):
            raise ValueError('Invalid value of entanglement: {}'.format(entanglement))

        # entanglement is List[List[List[List[int]]]]
        if all(isinstance(e4, int) for e in entanglement for e2 in e for e3 in e2 for e4 in e3):
            return entanglement[i % num_i][j % num_j]

        raise ValueError('Invalid value of entanglement: {}'.format(entanglement))

    @property
    def initial_state(self) -> Any:
        """Return the initial state that is added in front of the n-local circuit.

        Returns:
            The initial state.
        """
        return self._initial_state

    @initial_state.setter
    def initial_state(self, initial_state: Any) -> None:
        """Set the initial state.

        Args:
            initial_state: The new initial state.

        Raises:
            ValueError: If the number of qubits has been set before and the initial state
                does not match the number of qubits.
        """
        # If there is an initial state object, check that the number of qubits is compatible
        # construct the circuit immediately. If the InitialState could modify the number of qubits
        # we could also do this later at circuit construction.
        self._initial_state = initial_state

        # construct the circuit of the initial state
        self._initial_state_circuit = initial_state.construct_circuit(mode='circuit')

        # the initial state dictates the number of qubits since we do not have information
        # about on which qubits the initial state acts
        if self._num_qubits is not None and \
                self._initial_state_circuit.num_qubits != self._num_qubits:
            raise ValueError('Mismatching number of qubits in initial state and n-local circuit.')

        self._invalidate()

    @property
    def parameter_bounds(self) -> Optional[List[Tuple[float, float]]]:
        """The parameter bounds for the unbound parameters in the circuit.

        Returns:
            A list of pairs indicating the bounds, as (lower, upper). None indicates an unbounded
            parameter in the corresponding direction. If None is returned, problem is fully
            unbounded.
        """
        self._build()
        return self._bounds

    @parameter_bounds.setter
    def parameter_bounds(self, bounds: List[Tuple[float, float]]) -> None:
        """Set the parameter bounds.

        Args:
            bounds: The new parameter bounds.
        """
        self._bounds = bounds

    def _invalidate(self):
        """Invalidate the current circuit build."""
        self._data = None
        self._parameter_table = ParameterTable()

    def add_layer(self,
                  other: Union['NLocal', Instruction, QuantumCircuit],
                  entanglement: Optional[Union[List[int], str, List[List[int]]]] = None,
                  front: bool = False,
                  ) -> 'NLocal':
        """Append another layer to the NLocal.

        Args:
            other: The layer to compose, can be another NLocal, an Instruction or Gate,
                or a QuantumCircuit.
            entanglement: The entanglement or qubit indices.
            front: If True, ``other`` is appended to the front, else to the back.

        Returns:
            self, such that chained composes are possible.

        Raises:
            TypeError: If `other` is not compatible, i.e. is no Instruction and does not have a
                `to_instruction` method.
        """
        block = self._convert_to_block(other)

        if entanglement is None:
            entanglement = [list(range(block.num_qubits))]
        elif isinstance(entanglement, list) and not isinstance(entanglement[0], list):
            entanglement = [entanglement]

        if front:
            self._prepended_blocks += [block]
            self._prepended_entanglement += [entanglement]
        else:
            self._appended_blocks += [block]
            self._appended_entanglement += [entanglement]

        if isinstance(entanglement, list):
            num_qubits = 1 + max(max(indices) for indices in entanglement)
            if num_qubits > self.num_qubits:
                self._invalidate()  # rebuild circuit
                self.num_qubits = num_qubits

        # modify the circuit accordingly
        if self._data and front is False:
            if self._insert_barriers and len(self._data) > 0:
                self.barrier()

            if isinstance(entanglement, str):
                entangler_map = get_entangler_map(block.num_qubits, self.num_qubits, entanglement)
            else:
                entangler_map = entanglement

            layer = QuantumCircuit(self.num_qubits)
            for i in entangler_map:
                params = self.ordered_parameters[-len(get_parameters(block)):]
                parameterized_block = self._parameterize_block(block, params=params)
                layer.compose(parameterized_block, i)

            self += layer
        else:
            # cannot prepend a block currently, just rebuild
            self._invalidate()

        return self

    def assign_parameters(self, param_dict: Union[dict, List[float], List[Parameter],
                                                  ParameterVector],
                          inplace: bool = False) -> Optional[QuantumCircuit]:
        """Assign parameters to the n-local circuit.

        This method also supports passing a list instead of a dictionary. If a list
        is passed, the list must have the same length as the number of unbound parameters in
        the circuit. The parameters are assigned in the order of the parameters in
        :meth:`ordered_parameters`.

        Returns:
            A copy of the NLocal circuit with the specified parameters.

        Raises:
            AttributeError: If the parameters are given as list and do not match the number
                of parameters.
        """
        if self._data is None:
            self._build()

        if not isinstance(param_dict, dict):
            if len(param_dict) != self.num_parameters:
                raise AttributeError('If the parameters are provided as list, the size must match '
                                     'the number of parameters ({}), but {} are given.'.format(
                                         self.num_parameters, len(param_dict)
                                     ))
            unbound_params = [param for param in self._ordered_parameters if
                              isinstance(param, ParameterExpression)]

            # to get a sorted list of unique parameters, keep track of the already used parameters
            # in a set and add the parameters to the unique list only if not existing in the set
            used = set()
            unbound_unique_params = []
            for param in unbound_params:
                if param not in used:
                    unbound_unique_params.append(param)
                    used.add(param)

            param_dict = dict(zip(unbound_unique_params, param_dict))

        if inplace:
            new = [param_dict.get(param, param) for param in self.ordered_parameters]
            self._ordered_parameters = new

        return super().assign_parameters(param_dict, inplace=inplace)

    def _parameterize_block(self, block, param_iter=None, rep_num=None, block_num=None,
                            indices=None, params=None):
        """Convert ``block`` to a circuit of correct width and parameterized using the iterator."""
        if self._overwrite_block_parameters:
            # check if special parameters should be used
            # pylint: disable=assignment-from-none
            if params is None:
                params = self._parameter_generator(rep_num, block_num, indices)
            if params is None:
                params = [next(param_iter) for _ in range(len(get_parameters(block)))]

            update = dict(zip(block.parameters, params))
            return block.assign_parameters(update)

        return block.copy()

    def _build_rotation_layer(self, param_iter, i):
        """Build a rotation layer."""
        # if the unentangled qubits are skipped, compute the set of qubits that are not entangled
        if self._skip_unentangled_qubits:
            unentangled_qubits = self.get_unentangled_qubits()

        # iterate over all rotation blocks
        for j, block in enumerate(self.rotation_blocks):
            # create a new layer
            layer = QuantumCircuit(*self.qregs)

            # we apply the rotation gates stacked on top of each other, i.e.
            # if we have 4 qubits and a rotation block of width 2, we apply two instances
            block_indices = [
                list(range(k * block.num_qubits, (k + 1) * block.num_qubits))
                for k in range(self.num_qubits // block.num_qubits)
            ]

            # if unentangled qubits should not be acted on, remove all operations that
            # touch an unentangled qubit
            if self._skip_unentangled_qubits:
                block_indices = [indices for indices in block_indices
                                 if set(indices).isdisjoint(unentangled_qubits)]

            # apply the operations in the layer
            for indices in block_indices:
                parameterized_block = self._parameterize_block(block, param_iter, i, j, indices)
                layer.compose(parameterized_block, indices, inplace=True)

            # add the layer to the circuit
            self += layer

    def _build_entanglement_layer(self, param_iter, i):
        """Build an entanglement layer."""
        # iterate over all entanglement blocks
        for j, block in enumerate(self.entanglement_blocks):
            # create a new layer and get the entangler map for this block
            layer = QuantumCircuit(*self.qregs)
            entangler_map = self.get_entangler_map(i, j, block.num_qubits)

            # apply the operations in the layer
            for indices in entangler_map:
                parameterized_block = self._parameterize_block(block, param_iter, i, j, indices)
                layer.compose(parameterized_block, indices, inplace=True)

            # add the layer to the circuit
            self += layer

    def _build_additional_layers(self, which):
        if which == 'appended':
            blocks = self._appended_blocks
            entanglements = self._appended_entanglement
        elif which == 'prepended':
            blocks = reversed(self._prepended_blocks)
            entanglements = reversed(self._prepended_entanglement)
        else:
            raise ValueError('`which` must be either `appended` or `prepended`.')

        for block, ent in zip(blocks, entanglements):
            layer = QuantumCircuit(*self.qregs)
            if isinstance(ent, str):
                ent = get_entangler_map(block.num_qubits, self.num_qubits, ent)
            for indices in ent:
                layer.compose(block, indices, inplace=True)

            self += layer

    def _build(self) -> None:
        """Build the circuit."""
        if self._data:
            return

        _ = self._check_configuration()

        self._data = []

        if self.num_qubits == 0:
            return

        # use the initial state circuit if it is not None
        if self._initial_state:
            circuit = self._initial_state.construct_circuit('circuit', register=self.qregs[0])
            self += circuit

        param_iter = iter(self.ordered_parameters)

        # build the prepended layers
        self._build_additional_layers('prepended')

        # main loop to build the entanglement and rotation layers
        for i in range(self.reps):
            # insert barrier if specified and there is a preceding layer
            if self._insert_barriers and (i > 0 or len(self._prepended_blocks) > 0):
                self.barrier()

            # build the rotation layer
            self._build_rotation_layer(param_iter, i)

            # barrier in between rotation and entanglement layer
            if self._insert_barriers and len(self._rotation_blocks) > 0:
                self.barrier()

            # build the entanglement layer
            self._build_entanglement_layer(param_iter, i)

        # add the final rotation layer
        if not self._skip_final_rotation_layer:
            if self.insert_barriers:
                self.barrier()
            self._build_rotation_layer(param_iter, self.reps)

        # add the appended layers
        self._build_additional_layers('appended')

    # pylint: disable=unused-argument
    def _parameter_generator(self, rep: int, block: int, indices: List[int]) -> Optional[Parameter]:
        """If certain blocks should use certain parameters this method can be overriden."""
        return None

    def __str__(self) -> str:
        """Draw this NLocal in circuit format using the standard gates.

        Returns:
            A single string representing this NLocal.
        """
        from qiskit.compiler import transpile
        basis_gates = ['id', 'x', 'y', 'z', 'h', 's', 't', 'sdg', 'tdg', 'rx', 'ry', 'rz',
                       'rxx', 'ryy', 'cx', 'cy', 'cz', 'ch', 'crx', 'cry', 'crz', 'swap',
                       'cswap', 'ccx', 'cu1', 'cu3', 'u1', 'u2', 'u3']
        return transpile(self, basis_gates=basis_gates,
                         optimization_level=0).draw(output='text').single_string()
Пример #5
0
class RawFeatureVector(BlueprintCircuit):
    """The raw feature vector circuit.

    This circuit acts as parameterized initialization for statevectors with ``feature_dimension``
    dimensions, thus with ``log2(feature_dimension)`` qubits. The circuit contains a
    placeholder instruction that can only be synthesized/defined when all parameters are bound.

    In ML, this circuit can be used to load the training data into qubit amplitudes. It does not
    apply an kernel transformation (therefore, it is a "raw" feature vector).

    Since initialization is implemented via a ``QuantumCircuit.initialize()`` call, this circuit
    can't be used with gradient based optimizers, one can see a warning that gradients can't be
    computed.

    Examples:

    .. code-block::

        from qiskit_machine_learning.circuit.library import RawFeatureVector
        circuit = RawFeatureVector(4)
        print(circuit.num_qubits)
        # prints: 2

        print(circuit.draw(output='text'))
        # prints:
        #      ┌───────────────────────────────────────────────┐
        # q_0: ┤0                                              ├
        #      │  PARAMETERIZEDINITIALIZE(x[0],x[1],x[2],x[3]) │
        # q_1: ┤1                                              ├
        #      └───────────────────────────────────────────────┘

        print(circuit.ordered_parameters)
        # prints: [Parameter(p[0]), Parameter(p[1]), Parameter(p[2]), Parameter(p[3])]

        import numpy as np
        state = np.array([1, 0, 0, 1]) / np.sqrt(2)
        bound = circuit.assign_parameters(state)
        print(bound.draw())
        # prints:
        #      ┌───────────────────────────────────────────────┐
        # q_0: ┤0                                              ├
        #      │  PARAMETERIZEDINITIALIZE(0.70711,0,0,0.70711) │
        # q_1: ┤1                                              ├
        #      └───────────────────────────────────────────────┘

    """
    def __init__(self, feature_dimension: Optional[int]) -> None:
        """
        Args:
            feature_dimension: The feature dimension from which the number of
                                qubits is inferred as ``n_qubits = log2(feature_dim)``

        """
        super().__init__()

        self._ordered_parameters = ParameterVector("x")
        if feature_dimension is not None:
            self.feature_dimension = feature_dimension

    def _build(self):
        super()._build()

        placeholder = ParameterizedInitialize(self._ordered_parameters[:])
        self.append(placeholder, self.qubits)

    def _unsorted_parameters(self):
        if self.data is None:
            self._build()
        return super()._unsorted_parameters()

    def _check_configuration(self, raise_on_failure=True):
        if isinstance(self._ordered_parameters, ParameterVector):
            self._ordered_parameters.resize(self.feature_dimension)
        elif len(self._ordered_parameters) != self.feature_dimension:
            if raise_on_failure:
                raise ValueError(
                    "Mismatching number of parameters and feature dimension.")
            return False
        return True

    @property
    def num_qubits(self) -> int:
        """Returns the number of qubits in this circuit.

        Returns:
            The number of qubits.
        """
        return super().num_qubits

    @num_qubits.setter
    def num_qubits(self, num_qubits: int) -> None:
        """Set the number of qubits for the n-local circuit.

        Args:
            The new number of qubits.
        """
        if self.num_qubits != num_qubits:
            # invalidate the circuit
            self._invalidate()
            self.qregs: List[QuantumRegister] = []
            if num_qubits is not None and num_qubits > 0:
                self.qregs = [QuantumRegister(num_qubits, name="q")]

    @property
    def feature_dimension(self) -> int:
        """Return the feature dimension.

        Returns:
            The feature dimension, which is ``2 ** num_qubits``.
        """
        return 2**self.num_qubits

    @feature_dimension.setter
    def feature_dimension(self, feature_dimension: int) -> None:
        """Set the feature dimension.

        Args:
            feature_dimension: The new feature dimension. Must be a power of 2.

        Raises:
            ValueError: If ``feature_dimension`` is not a power of 2.
        """
        num_qubits = np.log2(feature_dimension)
        if int(num_qubits) != num_qubits:
            raise ValueError("feature_dimension must be a power of 2!")

        if num_qubits != self.num_qubits:
            self._invalidate()
            self.num_qubits = int(num_qubits)
Пример #6
0
    def _build(self):
        if self._data is not None:
            return

        self._check_configuration()
        self._data = []

        # get the evolved operators as circuits
        from qiskit.opflow import PauliOp

        coeff = Parameter("c")
        circuits = []
        is_evolved_operator = []
        for op in self.operators:
            # if the operator is already the evolved circuit just append it
            if isinstance(op, QuantumCircuit):
                circuits.append(op)
                is_evolved_operator.append(False)  # has no time coeff
            else:
                # check if the operator is just the identity, if yes, skip it
                if isinstance(op, PauliOp):
                    sig_qubits = np.logical_or(op.primitive.x, op.primitive.z)
                    if sum(sig_qubits) == 0:
                        continue

                evolved_op = self.evolution.convert(
                    (coeff * op).exp_i()).reduce()
                circuits.append(evolved_op.to_circuit())
                is_evolved_operator.append(True)  # has time coeff

        # set the registers
        num_qubits = circuits[0].num_qubits
        try:
            qr = QuantumRegister(num_qubits, "q")
            self.add_register(qr)
        except CircuitError:
            # the register already exists, probably because of a previous composition
            pass

        # build the circuit
        times = ParameterVector("t", self.reps * sum(is_evolved_operator))
        times_it = iter(times)

        first = True
        for _ in range(self.reps):
            for is_evolved, circuit in zip(is_evolved_operator, circuits):
                if first:
                    first = False
                else:
                    if self._insert_barriers:
                        self.barrier()

                if is_evolved:
                    bound = circuit.assign_parameters({coeff: next(times_it)})
                else:
                    bound = circuit

                self.compose(bound, inplace=True)

        if self.initial_state:
            self.compose(self.initial_state, front=True, inplace=True)
Пример #7
0
class TestNLocal(QiskitTestCase):
    """Test the n-local circuit class."""
    def test_if_reps_is_negative(self):
        """Test to check if error is raised for negative value of reps"""
        with self.assertRaises(ValueError):
            _ = NLocal(reps=-1)

    def test_if_reps_is_str(self):
        """Test to check if proper error is raised for str value of reps"""
        with self.assertRaises(TypeError):
            _ = NLocal(reps="3")

    def test_if_reps_is_float(self):
        """Test to check if proper error is raised for float value of reps"""
        with self.assertRaises(TypeError):
            _ = NLocal(reps=5.6)

    def test_if_reps_is_npint32(self):
        """Equality test for reps with int value and np.int32 value"""
        self.assertEqual(NLocal(reps=3), NLocal(reps=np.int32(3)))

    def test_if_reps_is_npint64(self):
        """Equality test for reps with int value and np.int64 value"""
        self.assertEqual(NLocal(reps=3), NLocal(reps=np.int64(3)))

    def test_reps_setter_when_negative(self):
        """Test to check if setter raises error for reps < 0"""
        nlocal = NLocal(reps=1)
        with self.assertRaises(ValueError):
            nlocal.reps = -1

    def assertCircuitEqual(self, qc1, qc2, visual=False, transpiled=True):
        """An equality test specialized to circuits."""
        if transpiled:
            basis_gates = ["id", "u1", "u3", "cx"]
            qc1_transpiled = transpile(qc1,
                                       basis_gates=basis_gates,
                                       optimization_level=0)
            qc2_transpiled = transpile(qc2,
                                       basis_gates=basis_gates,
                                       optimization_level=0)
            qc1, qc2 = qc1_transpiled, qc2_transpiled

        if visual:
            self.assertEqual(qc1.draw(), qc2.draw())
        else:
            self.assertEqual(qc1, qc2)

    def test_empty_nlocal(self):
        """Test the creation of an empty NLocal."""
        nlocal = NLocal()
        self.assertEqual(nlocal.num_qubits, 0)
        self.assertEqual(nlocal.num_parameters_settable, 0)
        self.assertEqual(nlocal.reps, 1)

        self.assertEqual(nlocal, QuantumCircuit())

        for attribute in [nlocal.rotation_blocks, nlocal.entanglement_blocks]:
            self.assertEqual(len(attribute), 0)

    @data(
        (XGate(), [[0], [2], [1]]),
        (XGate(), [[0]]),
        (CRXGate(-0.2), [[2, 0], [1, 3]]),
    )
    @unpack
    def test_add_layer_to_empty_nlocal(self, block, entangler_map):
        """Test appending gates to an empty nlocal."""
        nlocal = NLocal()
        nlocal.add_layer(block, entangler_map)

        max_num_qubits = max(max(indices) for indices in entangler_map)
        reference = QuantumCircuit(max_num_qubits + 1)
        for indices in entangler_map:
            reference.append(block, indices)

        self.assertCircuitEqual(nlocal, reference)

    @data([5, 3], [1, 5], [1, 1], [1, 2, 3, 10])
    def test_append_circuit(self, num_qubits):
        """Test appending circuits to an nlocal works normally."""
        # fixed depth of 3 gates per circuit
        depth = 3

        # keep track of a reference circuit
        reference = QuantumCircuit(max(num_qubits))

        # construct the NLocal from the first circuit
        first_circuit = random_circuit(num_qubits[0], depth, seed=4200)
        # TODO Terra bug: if this is to_gate it fails, since the QC adds an instruction not gate
        nlocal = NLocal(max(num_qubits),
                        entanglement_blocks=first_circuit.to_instruction(),
                        reps=1)
        reference.append(first_circuit, list(range(num_qubits[0])))

        # append the rest
        for num in num_qubits[1:]:
            circuit = random_circuit(num, depth, seed=4200)
            nlocal.append(circuit, list(range(num)))
            reference.append(circuit, list(range(num)))

        self.assertCircuitEqual(nlocal, reference)

    @data([5, 3], [1, 5], [1, 1], [1, 2, 3, 10])
    def test_add_nlocal(self, num_qubits):
        """Test adding an nlocal to an nlocal (using add_layer)."""
        # fixed depth of 3 gates per circuit
        depth = 3

        # keep track of a reference circuit
        reference = QuantumCircuit(max(num_qubits))

        # construct the NLocal from the first circuit
        first_circuit = random_circuit(num_qubits[0], depth, seed=4220)
        # TODO Terra bug: if this is to_gate it fails, since the QC adds an instruction not gate
        nlocal = NLocal(max(num_qubits),
                        entanglement_blocks=first_circuit.to_instruction(),
                        reps=1)
        reference.append(first_circuit, list(range(num_qubits[0])))

        # append the rest
        for num in num_qubits[1:]:
            circuit = random_circuit(num, depth, seed=4220)
            nlocal.add_layer(NLocal(num, entanglement_blocks=circuit, reps=1))
            reference.append(circuit, list(range(num)))

        self.assertCircuitEqual(nlocal, reference)

    @unittest.skip("Feature missing")
    def test_iadd_overload(self):
        """Test the overloaded + operator."""
        num_qubits, depth = 2, 2

        # construct two circuits for adding
        first_circuit = random_circuit(num_qubits, depth, seed=4242)
        circuit = random_circuit(num_qubits, depth, seed=4242)

        # get a reference
        reference = first_circuit + circuit

        # convert the object to be appended to different types
        others = [
            circuit,
            circuit.to_instruction(),
            circuit.to_gate(),
            NLocal(circuit)
        ]

        # try adding each type
        for other in others:
            nlocal = NLocal(num_qubits,
                            entanglement_blocks=first_circuit,
                            reps=1)
            nlocal += other
            with self.subTest(msg=f"type: {type(other)}"):
                self.assertCircuitEqual(nlocal, reference)

    def test_parameter_getter_from_automatic_repetition(self):
        """Test getting and setting of the nlocal parameters."""
        circuit = QuantumCircuit(2)
        circuit.ry(Parameter("a"), 0)
        circuit.crx(Parameter("b"), 0, 1)

        # repeat circuit and check that parameters are duplicated
        reps = 3
        nlocal = NLocal(2, entanglement_blocks=circuit, reps=reps)
        self.assertTrue(nlocal.num_parameters, 6)
        self.assertTrue(len(nlocal.parameters), 6)

    @data(list(range(6)), ParameterVector("θ", length=6),
          [0, 1, Parameter("theta"), 3, 4, 5])
    def test_parameter_setter_from_automatic_repetition(self, params):
        """Test getting and setting of the nlocal parameters."""
        circuit = QuantumCircuit(2)
        circuit.ry(Parameter("a"), 0)
        circuit.crx(Parameter("b"), 0, 1)

        # repeat circuit and check that parameters are duplicated
        reps = 3
        nlocal = NLocal(2, entanglement_blocks=circuit, reps=reps)
        nlocal.assign_parameters(params, inplace=True)

        param_set = {p for p in params if isinstance(p, ParameterExpression)}
        with self.subTest(
                msg="Test the parameters of the non-transpiled circuit"):
            # check the parameters of the final circuit
            self.assertEqual(nlocal.parameters, param_set)

        with self.subTest(msg="Test the parameters of the transpiled circuit"):
            basis_gates = ["id", "u1", "u2", "u3", "cx"]
            transpiled_circuit = transpile(nlocal, basis_gates=basis_gates)
            self.assertEqual(transpiled_circuit.parameters, param_set)

    @data(list(range(6)), ParameterVector("θ", length=6),
          [0, 1, Parameter("theta"), 3, 4, 5])
    def test_parameters_setter(self, params):
        """Test setting the parameters via list."""
        # construct circuit with some parameters
        initial_params = ParameterVector("p", length=6)
        circuit = QuantumCircuit(1)
        for i, initial_param in enumerate(initial_params):
            circuit.ry(i * initial_param, 0)

        # create an NLocal from the circuit and set the new parameters
        nlocal = NLocal(1, entanglement_blocks=circuit, reps=1)
        nlocal.assign_parameters(params, inplace=True)

        param_set = {p for p in params if isinstance(p, ParameterExpression)}
        with self.subTest(
                msg="Test the parameters of the non-transpiled circuit"):
            # check the parameters of the final circuit
            self.assertEqual(nlocal.parameters, param_set)

        with self.subTest(msg="Test the parameters of the transpiled circuit"):
            basis_gates = ["id", "u1", "u2", "u3", "cx"]
            transpiled_circuit = transpile(nlocal, basis_gates=basis_gates)
            self.assertEqual(transpiled_circuit.parameters, param_set)

    def test_repetetive_parameter_setting(self):
        """Test alternate setting of parameters and circuit construction."""
        x = Parameter("x")
        circuit = QuantumCircuit(1)
        circuit.rx(x, 0)

        nlocal = NLocal(1,
                        entanglement_blocks=circuit,
                        reps=3,
                        insert_barriers=True)
        with self.subTest(msg="immediately after initialization"):
            self.assertEqual(len(nlocal.parameters), 3)

        with self.subTest(msg="after circuit construction"):
            self.assertEqual(len(nlocal.parameters), 3)

        q = Parameter("q")
        nlocal.assign_parameters([x, q, q], inplace=True)
        with self.subTest(msg="setting parameter to Parameter objects"):
            self.assertEqual(nlocal.parameters, set({x, q}))

        nlocal.assign_parameters([0, -1], inplace=True)
        with self.subTest(msg="setting parameter to numbers"):
            self.assertEqual(nlocal.parameters, set())

    def test_skip_unentangled_qubits(self):
        """Test skipping the unentangled qubits."""
        num_qubits = 6
        entanglement_1 = [[0, 1, 3], [1, 3, 5], [0, 1, 5]]
        skipped_1 = [2, 4]

        entanglement_2 = [entanglement_1, [[0, 1, 2], [2, 3, 5]]]
        skipped_2 = [4]

        for entanglement, skipped in zip([entanglement_1, entanglement_2],
                                         [skipped_1, skipped_2]):
            with self.subTest(entanglement=entanglement, skipped=skipped):
                nlocal = NLocal(
                    num_qubits,
                    rotation_blocks=XGate(),
                    entanglement_blocks=CCXGate(),
                    entanglement=entanglement,
                    reps=3,
                    skip_unentangled_qubits=True,
                )

                skipped_set = {nlocal.qubits[i] for i in skipped}
                dag = circuit_to_dag(nlocal)
                idle = set(dag.idle_wires())
                self.assertEqual(skipped_set, idle)

    @data("linear", "full", "circular", "sca", ["linear", "full"],
          ["circular", "linear", "sca"])
    def test_entanglement_by_str(self, entanglement):
        """Test setting the entanglement of the layers by str."""
        reps = 3
        nlocal = NLocal(
            5,
            rotation_blocks=XGate(),
            entanglement_blocks=CCXGate(),
            entanglement=entanglement,
            reps=reps,
        )

        def get_expected_entangler_map(rep_num, mode):
            if mode == "linear":
                return [(0, 1, 2), (1, 2, 3), (2, 3, 4)]
            elif mode == "full":
                return [
                    (0, 1, 2),
                    (0, 1, 3),
                    (0, 1, 4),
                    (0, 2, 3),
                    (0, 2, 4),
                    (0, 3, 4),
                    (1, 2, 3),
                    (1, 2, 4),
                    (1, 3, 4),
                    (2, 3, 4),
                ]
            else:
                circular = [(3, 4, 0), (0, 1, 2), (1, 2, 3), (2, 3, 4)]
                if mode == "circular":
                    return circular
                sca = circular[-rep_num:] + circular[:-rep_num]
                if rep_num % 2 == 1:
                    sca = [tuple(reversed(indices)) for indices in sca]
                return sca

        for rep_num in range(reps):
            entangler_map = nlocal.get_entangler_map(rep_num, 0, 3)
            if isinstance(entanglement, list):
                mode = entanglement[rep_num % len(entanglement)]
            else:
                mode = entanglement
            expected = get_expected_entangler_map(rep_num, mode)

            with self.subTest(rep_num=rep_num):
                # using a set here since the order does not matter
                self.assertEqual(set(entangler_map), set(expected))

    def test_pairwise_entanglement(self):
        """Test pairwise entanglement."""
        nlocal = NLocal(
            5,
            rotation_blocks=XGate(),
            entanglement_blocks=CXGate(),
            entanglement="pairwise",
            reps=1,
        )
        entangler_map = nlocal.get_entangler_map(0, 0, 2)
        pairwise = [(0, 1), (2, 3), (1, 2), (3, 4)]

        self.assertEqual(pairwise, entangler_map)

    def test_pairwise_entanglement_raises(self):
        """Test choosing pairwise entanglement raises an error for too large blocks."""
        nlocal = NLocal(3, XGate(), CCXGate(), entanglement="pairwise", reps=1)

        # pairwise entanglement is only defined if the entangling gate has 2 qubits
        with self.assertRaises(ValueError):
            print(nlocal.draw())

    def test_entanglement_by_list(self):
        """Test setting the entanglement by list.

        This is the circuit we test (times 2, with final X layer)
                ┌───┐                ┌───┐┌───┐                  ┌───┐
        q_0: |0>┤ X ├──■────■───X────┤ X ├┤ X ├──■───X─────── .. ┤ X ├
                ├───┤  │    │   │    ├───┤└─┬─┘  │   │           ├───┤
        q_1: |0>┤ X ├──■────┼───┼──X─┤ X ├──■────┼───X──X──── .. ┤ X ├
                ├───┤┌─┴─┐  │   │  │ ├───┤  │    │      │     x2 ├───┤
        q_2: |0>┤ X ├┤ X ├──■───┼──X─┤ X ├──■────■──────X──X─ .. ┤ X ├
                ├───┤└───┘┌─┴─┐ │    ├───┤     ┌─┴─┐       │     ├───┤
        q_3: |0>┤ X ├─────┤ X ├─X────┤ X ├─────┤ X ├───────X─ .. ┤ X ├
                └───┘     └───┘      └───┘     └───┘             └───┘
        """
        circuit = QuantumCircuit(4)
        for _ in range(2):
            circuit.x([0, 1, 2, 3])
            circuit.barrier()
            circuit.ccx(0, 1, 2)
            circuit.ccx(0, 2, 3)
            circuit.swap(0, 3)
            circuit.swap(1, 2)
            circuit.barrier()
            circuit.x([0, 1, 2, 3])
            circuit.barrier()
            circuit.ccx(2, 1, 0)
            circuit.ccx(0, 2, 3)
            circuit.swap(0, 1)
            circuit.swap(1, 2)
            circuit.swap(2, 3)
            circuit.barrier()
        circuit.x([0, 1, 2, 3])

        layer_1_ccx = [(0, 1, 2), (0, 2, 3)]
        layer_1_swap = [(0, 3), (1, 2)]
        layer_1 = [layer_1_ccx, layer_1_swap]

        layer_2_ccx = [(2, 1, 0), (0, 2, 3)]
        layer_2_swap = [(0, 1), (1, 2), (2, 3)]
        layer_2 = [layer_2_ccx, layer_2_swap]

        entanglement = [layer_1, layer_2]

        nlocal = NLocal(
            4,
            rotation_blocks=XGate(),
            entanglement_blocks=[CCXGate(), SwapGate()],
            reps=4,
            entanglement=entanglement,
            insert_barriers=True,
        )

        self.assertCircuitEqual(nlocal, circuit)

    def test_initial_state_as_circuit_object(self):
        """Test setting `initial_state` to `QuantumCircuit` object"""
        ref = QuantumCircuit(2)
        ref.cx(0, 1)
        ref.x(0)
        ref.h(1)
        ref.x(1)
        ref.cx(0, 1)
        ref.x(0)
        ref.x(1)

        qc = QuantumCircuit(2)
        qc.cx(0, 1)
        qc.h(1)

        expected = NLocal(
            num_qubits=2,
            rotation_blocks=XGate(),
            entanglement_blocks=CXGate(),
            initial_state=qc,
            reps=1,
        )

        self.assertCircuitEqual(ref, expected)
    def _build(self):
        if self._data is not None:
            return

        self._check_configuration()
        self._data = []

        # get the evolved operators as circuits
        from qiskit.opflow import PauliOp

        coeff = Parameter("c")
        circuits = []
        is_evolved_operator = []
        for op in self.operators:
            # if the operator is already the evolved circuit just append it
            if isinstance(op, QuantumCircuit):
                circuits.append(op)
                is_evolved_operator.append(False)  # has no time coeff
            else:
                # check if the operator is just the identity, if yes, skip it
                if isinstance(op, PauliOp):
                    sig_qubits = np.logical_or(op.primitive.x, op.primitive.z)
                    if sum(sig_qubits) == 0:
                        continue

                evolved_op = self.evolution.convert((coeff * op).exp_i()).reduce()
                circuits.append(evolved_op.to_circuit())
                is_evolved_operator.append(True)  # has time coeff

        # set the registers
        num_qubits = circuits[0].num_qubits
        try:
            qr = QuantumRegister(num_qubits, "q")
            self.add_register(qr)
        except CircuitError:
            # the register already exists, probably because of a previous composition
            pass

        # build the circuit
        times = ParameterVector("t", self.reps * sum(is_evolved_operator))
        times_it = iter(times)

        evolution = QuantumCircuit(*self.qregs, name=self.name)

        first = True
        for _ in range(self.reps):
            for is_evolved, circuit in zip(is_evolved_operator, circuits):
                if first:
                    first = False
                else:
                    if self._insert_barriers:
                        evolution.barrier()

                if is_evolved:
                    bound = circuit.assign_parameters({coeff: next(times_it)})
                else:
                    bound = circuit

                evolution.compose(bound, inplace=True)

        if self.initial_state:
            evolution.compose(self.initial_state, front=True, inplace=True)

        # cast global phase to float if it has no free parameters
        if isinstance(evolution.global_phase, ParameterExpression):
            try:
                evolution.global_phase = float(evolution.global_phase._symbol_expr)
            # RuntimeError is raised if symengine is used, for SymPy it is a TypeError
            except (RuntimeError, TypeError):
                # expression contains free parameters
                pass

        try:
            instr = evolution.to_gate()
        except QiskitError:
            instr = evolution.to_instruction()

        self.append(instr, self.qubits)
Пример #9
0
    def __init__(self,
                 initial_state: InitialState,
                 operator: Optional[BaseOperator] = None,
                 q: Optional[float] = 0.5,
                 num_ancillae: Optional[int] = 0,
                 var_form: Optional[VariationalForm] = None,
                 optimizer: Optional[Optimizer] = None,
                 initial_point: Optional[np.ndarray] = None,
                 max_evals_grouped: int = 1,
                 callback: Optional[Callable[[int, np.ndarray, float, float], None]] = None,
                 quantum_instance: Optional[Union[QuantumInstance, BaseBackend]] = None) -> None:
        """
        Constructor.

        Args:
            initial_state (InitialState): The state to be diagonalized
            operator (BaseOperator): The density matrix of the
            initial state
            q (int): Free parameter that ones to tailer the VQSD method
            num_ancillae (int): The number of ancillae qubits if the initial
            state is a mixed state
            var_form: A parameterized variational form (ansatz).
            optimizer: A classical optimizer.
            initial_point: An optional initial point (i.e. initial parameter values)
                for the optimizer. If ``None`` then VQE will look to the variational form for a
                preferred point and if not will simply compute a random one.
            max_evals_grouped: Max number of evaluations performed simultaneously. Signals the
                given optimizer that more than one set of parameters can be supplied so that
                potentially the expectation values can be computed in parallel. Typically this is
                possible when a finite difference gradient is used by the optimizer such that
                multiple points to compute the gradient can be passed and if computed in parallel
                improve overall execution time.
            callback: a callback that can access the intermediate data during the optimization.
                Four parameter values are passed to the callback as follows during each evaluation
                by the optimizer for its current set of parameters as it works towards the minimum.
                These are: the evaluation count, the optimizer parameters for the
                variational form, the evaluated global cost, local cost and
                weighted cost
            quantum_instance: Quantum Instance or Backend
        """

        validate_min('max_evals_grouped', max_evals_grouped, 1)
        validate_range('num_ancillae', num_ancillae, 0, initial_state._num_qubits - 1)
        validate_range('q', q, 0.0, 1.0)

        if var_form is None:
            # TODO after ansatz refactor num qubits can be set later so we do not have to have
            #      an operator to create a default
            if initial_state is not None:
                var_form = RY(initial_state._num_qubits -
                              num_ancillae)

        if optimizer is None:
            optimizer = SLSQP()

        if operator is None:
            initial_state_vector = initial_state.construct_circuit(mode='vector')
            mat = np.outer(initial_state_vector, np.conj(initial_state_vector))
            operator = DensityMatrix(mat)

        # TODO after ansatz refactor we may still not be able to do this
        # if num qubits is not set on var form
        if initial_point is None and var_form is not None:
            initial_point = var_form.preferred_init_points

        self._max_evals_grouped = max_evals_grouped

        super().__init__(var_form=var_form,
                         optimizer=optimizer,
                         cost_fn=self._cost_evaluation,
                         initial_point=initial_point,
                         quantum_instance=quantum_instance)

        self._callback = callback
        self._use_simulator_snapshot_mode = None
        self._ret = None
        self._eval_time = None
        self._eval_count = 0

        logger.info(self.print_settings())
        self._var_form_params = None
        if self.var_form is not None:
            self._var_form_params = ParameterVector('θ', self.var_form.num_parameters)
        self._parameterized_circuits = None

        self._initial_state = initial_state
        self._q = q
        self._num_ancillae = num_ancillae
        self._num_working_qubits = initial_state._num_qubits - num_ancillae
        self._operator = operator
        self.initial_state = initial_state

        # TODO : Verify that if the ancillae qubits form an orthonormal basis

        # Compute state purity
        if self._num_ancillae > 0:
            # pylint: disable=import-outside-toplevel
            from qiskit.quantum_info import purity, partial_trace

            rho = self._operator.data
            ancillae_idx = list(set(range(self._initial_state._num_qubits)) -
                                set(range(self._num_ancillae)))
            self._operator = partial_trace(rho, ancillae_idx)
            self._purity = purity(self._operator)
        else:
            self._purity = 1.0
Пример #10
0
 def var_form(self, var_form: VariationalForm):
     """ Sets variational form """
     self._var_form = var_form
     if var_form:
         self._var_form_params = ParameterVector('θ',
                                                 var_form.num_parameters)
    def __init__(self,name,layer):
        self.backend = Aer.get_backend('statevector_simulator');
        self.circ = QuantumCircuit(layer);
        self.name = name;
        self.seed = 14256;
        self.layer = layer;
        np.random.seed(self.seed);
        if self.name == "rcz":
            self.theta = Parameter('θ');
            for index in range(layer):
                self.circ.h(index);
            c = QuantumCircuit(1,name="Rz");
            c.rz(self.theta,0);
            temp = c.to_gate().control(1);
            self.circ.append(temp,[0,1]);
        if self.name == "circ4":
            self.theta1 = ParameterVector('θ1', layer);
            self.theta2 = ParameterVector('θ2', layer);
            self.theta3 = ParameterVector('θ3', layer-1);
            # print(self.theta1)
            for i in range(self.layer):
                self.circ.rx(self.theta1[i],i);
            for i in range(self.layer):
                self.circ.rz(self.theta2[i],i);
            for i in range(self.layer-1):
                c = QuantumCircuit(1, name="Rx");
                c.rx(self.theta3[i],0);
                temp = c.to_gate().control(1);
                self.circ.append(temp,[i,i+1]);
        if self.name == "circ4c":
            self.theta1 = ParameterVector('θ1', layer);
            self.theta2 = ParameterVector('θ2', layer);
            self.theta3 = ParameterVector('θ3', layer);
            # print(self.theta1)
            for i in range(self.layer):
                self.circ.rx(self.theta1[i],i);
            for i in range(self.layer):
                self.circ.rz(self.theta2[i],i);
            for i in range(self.layer-1):
                c = QuantumCircuit(1, name="Rx");
                c.rx(self.theta3[i],0);
                temp = c.to_gate().control(1);
                self.circ.append(temp,[i,i+1]);
            c = QuantumCircuit(1, name="Rx");
            c.rx(self.theta3[self.layer-1],0);
            temp = c.to_gate().control(1);
            self.circ.append(temp,[self.layer-1,0]);
        if self.name == "circ19":
            self.theta1 = ParameterVector('θ1', 3*layer);
            for i in range(self.layer):
                self.circ.rx(self.theta1[i],i);
            for i in range(self.layer):
                self.circ.rz(self.theta1[self.layer+i],i);
            for i in range(self.layer-1):
                c = QuantumCircuit(1, name="Rx");
                c.rx(self.theta1[self.layer*2+i],0);
                temp = c.to_gate().control(1);
                self.circ.append(temp,[i,i+1]);
            c = QuantumCircuit(1, name="Rx");
            c.rx(self.theta1[self.layer*3-1],0);
            temp = c.to_gate().control(1);
            self.circ.append(temp,[self.layer-1,0]);
        if self.name == "2circ19":
            self.theta1 = ParameterVector('θ1', 3*layer);
            self.theta2 = ParameterVector('θ2', 3*layer);
            for i in range(self.layer):
                self.circ.rx(self.theta1[i],i);
            for i in range(self.layer):
                self.circ.rz(self.theta1[self.layer+i],i);
            for i in range(self.layer-1):
                c = QuantumCircuit(1, name="Rx");
                c.rx(self.theta1[self.layer*2+i],0);
                temp = c.to_gate().control(1);
                self.circ.append(temp,[i,i+1]);
            c = QuantumCircuit(1, name="Rx");
            c.rx(self.theta1[self.layer*3-1],0);
            temp = c.to_gate().control(1);
            self.circ.append(temp,[self.layer-1,0]);
            # self.circ.barrier();
            for i in range(self.layer):
                self.circ.rx(self.theta2[i],i);
            for i in range(self.layer):
                self.circ.rz(self.theta2[self.layer+i],i);
            for i in range(self.layer-1):
                c = QuantumCircuit(1, name="Rx");
                c.rx(self.theta2[self.layer*2+i],0);
                temp = c.to_gate().control(1);
                self.circ.append(temp,[i,i+1]);
            c = QuantumCircuit(1, name="Rx");
            c.rx(self.theta2[self.layer*3-1],0);
            temp = c.to_gate().control(1);
            self.circ.append(temp,[self.layer-1,0]);
        
        if self.name == "2circ19x":
            self.theta1 = ParameterVector('θ1', 3*layer);
            self.theta2 = ParameterVector('θ2', 3*layer);
            for i in range(self.layer):
                self.circ.rx(self.theta1[i],i);
            for i in range(self.layer):
                self.circ.rz(self.theta1[self.layer+i],i);
            for i in range(self.layer-1):
                c = QuantumCircuit(1, name="Rx");
                c.rx(self.theta1[self.layer*2+i],0);
                temp = c.to_gate().control(1);
                self.circ.append(temp,[i,i+1]);
            c = QuantumCircuit(1, name="Rx");
            c.rx(self.theta1[self.layer*3-1],0);
            temp = c.to_gate().control(1);
            self.circ.append(temp,[self.layer-1,0]);
            # self.circ.barrier();
            for i in range(self.layer):
                self.circ.x(i);
            # self.circ.barrier();
            for i in range(self.layer):
                self.circ.rx(self.theta2[i],i);
            for i in range(self.layer):
                self.circ.rz(self.theta2[self.layer+i],i);
            for i in range(self.layer-1):
                c = QuantumCircuit(1, name="Rx");
                c.rx(self.theta2[self.layer*2+i],0);
                temp = c.to_gate().control(1);
                self.circ.append(temp,[i,i+1]);
            c = QuantumCircuit(1, name="Rx");
            c.rx(self.theta2[self.layer*3-1],0);
            temp = c.to_gate().control(1);
            self.circ.append(temp,[self.layer-1,0]);

        if self.name == "circ6":
            
            self.theta1 = ParameterVector('θ1', layer);
            self.theta2 = ParameterVector('θ2', layer);
            self.theta3 = ParameterVector('θ3', layer*(layer-1));
            self.theta4 = ParameterVector('θ4', layer);
            self.theta5 = ParameterVector('θ5', layer);
            
            # print(self.theta1)
            for i in range(self.layer):
                self.circ.rx(self.theta1[i],i);
            for i in range(self.layer):
                self.circ.rz(self.theta2[i],i);
            for i in range(self.layer):
                for j in range(self.layer):
                    if i<j:
                        c = QuantumCircuit(1, name="Rx");
                        c.rx(self.theta3[(self.layer-1)*i+j-1],0);
                        temp = c.to_gate().control(1);
                        self.circ.append(temp,[i,j]);
                    elif i>j:
                        c = QuantumCircuit(1, name="Rx");
                        c.rx(self.theta3[(self.layer-1)*i+j],0);
                        temp = c.to_gate().control(1);
                        self.circ.append(temp,[i,j]);
            self.circ.barrier();
            for i in range(self.layer):
                self.circ.rx(self.theta4[i],i);
            for i in range(self.layer):
                self.circ.rz(self.theta5[i],i);


        if self.name == "circ19half":
            self.theta1 = ParameterVector('θ1', layer);
            self.theta2 = ParameterVector('θ2', layer);
            self.theta3 = ParameterVector('θ3', layer);
            # print(self.theta1)
            for i in range(self.layer):
                self.circ.rx(self.theta1[i],i);
            for i in range(self.layer):
                self.circ.rz(self.theta2[i],i);
            for i in range(self.layer-1):
                c = QuantumCircuit(1, name="Rx");
                c.rx(self.theta3[i]/2,0);
                temp = c.to_gate().control(1);
                self.circ.append(temp,[i,i+1]);
            c = QuantumCircuit(1, name="Rx");
            c.rx(self.theta3[self.layer-1]/2,0);
            temp = c.to_gate().control(1);
            self.circ.append(temp,[self.layer-1,0]);
            for i in range(self.layer-1):
                c = QuantumCircuit(1, name="Rx");
                c.rx(self.theta3[i]/2,0);
                temp = c.to_gate().control(1);
                self.circ.append(temp,[i,i+1]);
            c = QuantumCircuit(1, name="Rx");
            c.rx(self.theta3[self.layer-1]/2,0);
            temp = c.to_gate().control(1);
            self.circ.append(temp,[self.layer-1,0]);

        if self.name == "circ4ca":
            self.theta1 = ParameterVector('θ1', layer);
            self.theta2 = ParameterVector('θ2', layer);
            self.theta3 = ParameterVector('θ3', layer);
            self.theta4 = ParameterVector('θ4', layer);
            self.theta5 = ParameterVector('θ5', layer);
            # print(self.theta1)
            for i in range(self.layer):
                self.circ.rx(self.theta1[i],i);
            for i in range(self.layer):
                self.circ.rz(self.theta2[i],i);
            for i in range(self.layer-1):
                c = QuantumCircuit(1, name="Rx");
                c.rx(self.theta3[i],0);
                temp = c.to_gate().control(1);
                self.circ.append(temp,[i,i+1]);
            c = QuantumCircuit(1, name="Rx");
            c.rx(self.theta3[self.layer-1],0);
            temp = c.to_gate().control(1);
            self.circ.append(temp,[self.layer-1,0]);
            for i in range(self.layer):
                self.circ.rx(self.theta4[i],i);
            for i in range(self.layer):
                self.circ.rz(self.theta5[i],i);

        if self.name == "V":
            self.theta = ParameterVector('θ',layer);
            self.phi = ParameterVector('φ',layer);
            self.alpha = ParameterVector('⍺',layer);
            for i in range(layer):
                self.circ.h(i);
            for i in range(layer):
                V(self.circ,self.theta[i],self.phi[i],self.alpha[i],i);
        if self.name == "V1":
            self.theta0 = ParameterVector('θ0',layer);
            self.theta1 = ParameterVector('θ1',layer);
            self.theta = ParameterVector('θ',layer);
            self.phi = ParameterVector('φ',layer);
            self.alpha = ParameterVector('⍺',layer);
            for i in range(layer):
                self.circ.rx(self.theta0[i],i);
            for i in range(layer):
                self.circ.rz(self.theta1[i],i);
            for i in range(layer):
                V(self.circ,self.theta[i],self.phi[i],self.alpha[i],i);
        if self.name == "circularcV":
            self.theta0 = ParameterVector('θ0',layer);
            self.theta1 = ParameterVector('θ1',layer);
            self.theta = ParameterVector('θ',layer);
            self.phi = ParameterVector('φ',layer);
            self.alpha = ParameterVector('⍺',layer);
            for i in range(layer):
                self.circ.rx(self.theta0[i],i);
            for i in range(layer):
                self.circ.rz(self.theta1[i],i);
            for i in range(layer-1):
                c = QuantumCircuit(1,name="V");
                V(c,self.theta[i],self.phi[i],self.alpha[i],0);
                temp = c.to_gate().control(1);
                self.circ.append(temp,[i,i+1]);
            c = QuantumCircuit(1,name="V");
            V(c,self.theta[layer-1],self.phi[layer-1],self.alpha[layer-1],0);
            temp = c.to_gate().control(1);
            self.circ.append(temp,[layer-1,0]);
            
        if self.name == "new0":
            # U gate가 진짜 uniform한지 확인하기 위함입니다.
            self.y = ParameterVector('θ_i',layer);
            self.z = ParameterVector('ϕ_i',layer);
            self.eta = ParameterVector('η', layer);
            self.phi = ParameterVector('ϕ', layer);
            self.t = ParameterVector('t',layer);
            for i in range(self.layer):
                self.circ.ry(self.y[i],i);
                self.circ.rz(self.z[i],i);
            for i in range(self.layer):
                self.circ.u3(self.eta[i],self.phi[i],self.t[i],i);
        
        if self.name == "new1":
            self.eta = ParameterVector('η', layer-1);
            self.phi = ParameterVector('ϕ', layer-1);
            self.t = ParameterVector('t',layer-1);
            for i in range(self.layer):
                self.circ.h(i);
            for i in range(self.layer-1):
                c = QuantumCircuit(1,name="U");             # 1 qubit짜리 회로를 생성합니다
                c.u3(self.eta[i],self.phi[i],self.t[i],0);  # U gate를 적용시킵니다.
                temp = c.to_gate().control(1);              # 윗 두 줄에서 만든 회로를 1개의 qubit이 control할 것이라고 말해줍니다
                self.circ.append(temp,[i,i+1]);             # 원래의 회로에 위치를 지정해서 추가합니다
        if self.name == "new2":
            self.eta1 = ParameterVector('η1', layer-1);
            self.phi1 = ParameterVector('ϕ1', layer-1);
            self.t1 = ParameterVector('t1',layer-1);
            self.eta2 = ParameterVector('η2', layer-1);
            self.phi2 = ParameterVector('ϕ2', layer-1);
            self.t2 = ParameterVector('t2',layer-1);
            for i in range(self.layer):
                self.circ.h(i);
            for i in range(self.layer-1):
                c = QuantumCircuit(1,name="U");
                c.u3(self.eta1[i],self.phi1[i],self.t1[i],0);
                temp = c.to_gate().control(1);
                self.circ.append(temp,[i,i+1]);
            for i in range(self.layer-1):
                c = QuantumCircuit(1,name="U");
                c.u3(self.eta2[i],self.phi2[i],self.t2[i],0);
                temp = c.to_gate().control(1);
                self.circ.append(temp,[i,i+1]);
        if self.name == "new3":
            self.eta = ParameterVector('η', layer);
            self.phi = ParameterVector('ϕ', layer);
            self.t = ParameterVector('t',layer);
            for i in range(self.layer):
                self.circ.h(i);
            for i in range(self.layer-1):
                c = QuantumCircuit(1,name="U");
                c.u3(self.eta[i],self.phi[i],self.t[i],0);
                temp = c.to_gate().control(1);
                self.circ.append(temp,[i,i+1]);
            c = QuantumCircuit(1,name="U");
            c.u3(self.eta[self.layer-1],self.phi[self.layer-1],self.t[self.layer-1],0);
            temp = c.to_gate().control(1);
            self.circ.append(temp,[self.layer-1,0]);
        if self.name == "new4":
            self.eta1 = ParameterVector('η1', layer);
            self.phi1 = ParameterVector('ϕ1', layer);
            self.t1 = ParameterVector('t1',layer);
            self.eta2 = ParameterVector('η2', layer);
            self.phi2 = ParameterVector('ϕ2', layer);
            self.t2 = ParameterVector('t2',layer);
            for i in range(self.layer):
                self.circ.h(i);
            for i in range(self.layer-1):
                c = QuantumCircuit(1,name="U");
                c.u3(self.eta1[i],self.phi1[i],self.t1[i],0);
                temp = c.to_gate().control(1);
                self.circ.append(temp,[i,i+1]);
            c = QuantumCircuit(1,name="U");
            c.u3(self.eta1[self.layer-1],self.phi1[self.layer-1],self.t1[self.layer-1],0);
            temp = c.to_gate().control(1);
            self.circ.append(temp,[self.layer-1,0]);
            for i in range(self.layer-1):
                c = QuantumCircuit(1,name="U");
                c.u3(self.eta2[i],self.phi2[i],self.t2[i],0);
                temp = c.to_gate().control(1);
                self.circ.append(temp,[i,i+1]);
            c = QuantumCircuit(1,name="U");
            c.u3(self.eta2[self.layer-1],self.phi2[self.layer-1],self.t2[self.layer-1],0);
            temp = c.to_gate().control(1);
            self.circ.append(temp,[self.layer-1,0]);

        if self.name == "new5":
            self.y = ParameterVector('θ_i',layer);
            self.z = ParameterVector('ϕ_i',layer);
            self.eta = ParameterVector('η', layer);
            self.phi = ParameterVector('ϕ', layer);
            self.t = ParameterVector('t',layer);
            for i in range(self.layer):
                self.circ.ry(self.y[i],i);
                self.circ.rz(self.z[i],i);
            for i in range(self.layer-1):
                c = QuantumCircuit(1,name="U");
                c.u3(self.eta[i],self.phi[i],self.t[i],0);
                temp = c.to_gate().control(1);
                self.circ.append(temp,[i,i+1]);
            c = QuantumCircuit(1,name="U");
            c.u3(self.eta[self.layer-1],self.phi[self.layer-1],self.t[self.layer-1],0);
            temp = c.to_gate().control(1);
            self.circ.append(temp,[self.layer-1,0]);

        if self.name == "new6":
            self.eta1 = ParameterVector('η1', layer-1);
            self.phi1 = ParameterVector('ϕ1', layer-1);
            self.t1 = ParameterVector('t1',layer-1);
            for i in range(self.layer):
                self.circ.h(i);
            for i in range(self.layer-1):
                self.circ.cx(i,i+1);
            for i in range(self.layer-1):
                c = QuantumCircuit(1,name="U");
                c.u3(self.eta1[i],self.phi1[i],self.t1[i],0);
                temp = c.to_gate().control(1);
                self.circ.append(temp,[i,i+1]);
Пример #12
0
    def test_save_and_load_model(self, use_circuits):
        """ save and load model test """
        aqua_globals.random_seed = self.seed
        backend = BasicAer.get_backend('qasm_simulator')

        num_qubits = 2
        optimizer = SPSA(max_trials=10,
                         save_steps=1,
                         c0=4.0,
                         skip_calibration=True)
        feature_map = SecondOrderExpansion(feature_dimension=num_qubits,
                                           depth=2)
        var_form = RYRZ(num_qubits=num_qubits, depth=3)

        # convert to circuit if circuits should be used
        if use_circuits:
            x = ParameterVector('x', feature_map.feature_dimension)
            feature_map = feature_map.construct_circuit(x)
            theta = ParameterVector('theta', var_form.num_parameters)
            var_form = var_form.construct_circuit(theta)

        # set up algorithm
        vqc = VQC(optimizer, feature_map, var_form, self.training_data,
                  self.testing_data)

        # sort parameters for reproducibility
        if use_circuits:
            vqc._feature_map_params = list(x)
            vqc._var_form_params = list(theta)

        quantum_instance = QuantumInstance(backend,
                                           shots=1024,
                                           seed_simulator=self.seed,
                                           seed_transpiler=self.seed)
        result = vqc.run(quantum_instance)

        np.testing.assert_array_almost_equal(result['opt_params'],
                                             self.ref_opt_params,
                                             decimal=4)
        np.testing.assert_array_almost_equal(result['training_loss'],
                                             self.ref_train_loss,
                                             decimal=8)

        self.assertEqual(1.0, result['testing_accuracy'])

        file_path = self.get_resource_path('vqc_test.npz')
        vqc.save_model(file_path)

        self.assertTrue(os.path.exists(file_path))

        loaded_vqc = VQC(optimizer, feature_map, var_form, self.training_data,
                         None)

        # sort parameters for reproducibility
        if use_circuits:
            loaded_vqc._feature_map_params = list(x)
            loaded_vqc._var_form_params = list(theta)

        loaded_vqc.load_model(file_path)

        np.testing.assert_array_almost_equal(loaded_vqc.ret['opt_params'],
                                             self.ref_opt_params,
                                             decimal=4)

        loaded_test_acc = loaded_vqc.test(vqc.test_dataset[0],
                                          vqc.test_dataset[1],
                                          quantum_instance)
        self.assertEqual(result['testing_accuracy'], loaded_test_acc)

        predicted_probs, predicted_labels = loaded_vqc.predict(
            self.testing_data['A'], quantum_instance)
        np.testing.assert_array_almost_equal(predicted_probs,
                                             self.ref_prediction_a_probs,
                                             decimal=8)
        np.testing.assert_array_equal(predicted_labels,
                                      self.ref_prediction_a_label)

        if os.path.exists(file_path):
            try:
                os.remove(file_path)
            except Exception:  # pylint: disable=broad-except
                pass
Пример #13
0
    def _build(self) -> None:
        """
        Construct the ansatz, given its parameters.

        Returns:
            QuantumCircuit: a quantum circuit with given `parameters`

        Raises:
            ValueError: only supports single and double excitations at the moment.
        """
        if self._is_built:
            return

        super()._build()
        self.data.clear()

        parameters = ParameterVector("θ", self._num_parameters)
        q = self.qubits

        if isinstance(self._initial_state, QuantumCircuit):
            self.append(self._initial_state.to_gate(),
                        range(self._initial_state.num_qubits))

        count = 0
        for _ in range(self._reps):
            for exc in self.excitations:
                occ, unocc = exc
                if len(occ) == 1:

                    i = occ[0]
                    r = unocc[0]

                    self.p(-parameters[count] / 4 + np.pi / 4, q[i])
                    self.p(-parameters[count] / 4 - np.pi / 4, q[r])

                    self.h(q[i])
                    self.h(q[r])

                    if self._ladder:
                        for qubit in range(i, r):
                            self.cx(q[qubit], q[qubit + 1])
                    else:
                        self.cx(q[i], q[r])

                    self.p(parameters[count], q[r])

                    if self._ladder:
                        for qubit in range(r, i, -1):
                            self.cx(q[qubit - 1], q[qubit])
                    else:
                        self.cx(q[i], q[r])

                    self.h(q[i])
                    self.h(q[r])

                    self.p(-parameters[count] / 4 - np.pi / 4, q[i])
                    self.p(-parameters[count] / 4 + np.pi / 4, q[r])

                elif len(occ) == 2:

                    i = occ[0]
                    r = unocc[0]
                    j = occ[1]
                    s = unocc[1]  # pylint: disable=invalid-name

                    self.sdg(q[r])

                    self.h(q[i])
                    self.h(q[r])
                    self.h(q[j])
                    self.h(q[s])

                    if self._ladder:
                        for qubit in range(i, r):
                            self.cx(q[qubit], q[qubit + 1])
                            self.barrier(q[qubit], q[qubit + 1])
                    else:
                        self.cx(q[i], q[r])
                    self.cx(q[r], q[j])
                    if self._ladder:
                        for qubit in range(j, s):
                            self.cx(q[qubit], q[qubit + 1])
                            self.barrier(q[qubit], q[qubit + 1])
                    else:
                        self.cx(q[j], q[s])

                    self.p(parameters[count], q[s])

                    if self._ladder:
                        for qubit in range(s, j, -1):
                            self.cx(q[qubit - 1], q[qubit])
                            self.barrier(q[qubit - 1], q[qubit])
                    else:
                        self.cx(q[j], q[s])
                    self.cx(q[r], q[j])
                    if self._ladder:
                        for qubit in range(r, i, -1):
                            self.cx(q[qubit - 1], q[qubit])
                            self.barrier(q[qubit - 1], q[qubit])
                    else:
                        self.cx(q[i], q[r])

                    self.h(q[i])
                    self.h(q[r])
                    self.h(q[j])
                    self.h(q[s])

                    self.p(-parameters[count] / 2 + np.pi / 2, q[i])
                    self.p(-parameters[count] / 2 + np.pi, q[r])

                else:
                    raise ValueError(
                        "Limited to single and double excitations, "
                        "higher order is not implemented")

                count += 1
Пример #14
0
    def get_kernel_matrix(quantum_instance,
                          feature_map,
                          x1_vec,
                          x2_vec=None,
                          enforce_psd=True):
        """
        Construct kernel matrix, if x2_vec is None, self-innerproduct is conducted.

        Notes:
            When using `statevector_simulator`,
            we only build the circuits for Psi(x1)|0> rather than
            Psi(x2)^dagger Psi(x1)|0>, and then we perform the inner product classically.
            That is, for `statevector_simulator`,
            the total number of circuits will be O(N) rather than
            O(N^2) for `qasm_simulator`.

        Args:
            quantum_instance (QuantumInstance): quantum backend with all settings
            feature_map (FeatureMap): a feature map that maps data to feature space
            x1_vec (numpy.ndarray): data points, 2-D array, N1xD, where N1 is the number of data,
                                    D is the feature dimension
            x2_vec (numpy.ndarray): data points, 2-D array, N2xD, where N2 is the number of data,
                                    D is the feature dimension
            enforce_psd (bool): enforces that the kernel matrix is positive semi-definite by setting
                                negative eigenvalues to zero. This is only applied in the symmetric
                                case, i.e., if `x2_vec == None`.
        Returns:
            numpy.ndarray: 2-D matrix, N1xN2
        """

        if isinstance(feature_map, QuantumCircuit):
            use_parameterized_circuits = True
        else:
            use_parameterized_circuits = feature_map.support_parameterized_circuit

        if x2_vec is None:
            is_symmetric = True
            x2_vec = x1_vec
        else:
            is_symmetric = False

        is_statevector_sim = quantum_instance.is_statevector

        measurement = not is_statevector_sim
        measurement_basis = '0' * feature_map.num_qubits
        mat = np.ones((x1_vec.shape[0], x2_vec.shape[0]))

        # get all indices
        if is_symmetric:
            mus, nus = np.triu_indices(x1_vec.shape[0],
                                       k=1)  # remove diagonal term
        else:
            mus, nus = np.indices((x1_vec.shape[0], x2_vec.shape[0]))
            mus = np.asarray(mus.flat)
            nus = np.asarray(nus.flat)

        if is_statevector_sim:
            if is_symmetric:
                to_be_computed_data = x1_vec
            else:
                to_be_computed_data = np.concatenate((x1_vec, x2_vec))

            if use_parameterized_circuits:
                # build parameterized circuits, it could be slower for building circuit
                # but overall it should be faster since it only transpile one circuit
                feature_map_params = ParameterVector(
                    'x', feature_map.feature_dimension)
                parameterized_circuit = QSVM._construct_circuit(
                    (feature_map_params, feature_map_params),
                    feature_map,
                    measurement,
                    is_statevector_sim=is_statevector_sim)
                parameterized_circuit = quantum_instance.transpile(
                    parameterized_circuit)[0]
                circuits = [
                    parameterized_circuit.assign_parameters(
                        {feature_map_params: x}) for x in to_be_computed_data
                ]
            else:
                #  the second x is redundant
                to_be_computed_data_pair = [(x, x)
                                            for x in to_be_computed_data]
                if logger.isEnabledFor(logging.DEBUG):
                    logger.debug("Building circuits:")
                    TextProgressBar(sys.stderr)
                circuits = parallel_map(
                    QSVM._construct_circuit,
                    to_be_computed_data_pair,
                    task_args=(feature_map, measurement, is_statevector_sim),
                    num_processes=aqua_globals.num_processes)

            results = quantum_instance.execute(
                circuits, had_transpiled=use_parameterized_circuits)

            if logger.isEnabledFor(logging.DEBUG):
                logger.debug("Calculating overlap:")
                TextProgressBar(sys.stderr)

            offset = 0 if is_symmetric else len(x1_vec)
            matrix_elements = parallel_map(
                QSVM._compute_overlap,
                list(zip(mus, nus + offset)),
                task_args=(results, is_statevector_sim, measurement_basis),
                num_processes=aqua_globals.num_processes)

            for i, j, value in zip(mus, nus, matrix_elements):
                mat[i, j] = value
                if is_symmetric:
                    mat[j, i] = mat[i, j]
        else:
            for idx in range(0, len(mus), QSVM.BATCH_SIZE):
                to_be_computed_data_pair = []
                to_be_computed_index = []
                for sub_idx in range(idx, min(idx + QSVM.BATCH_SIZE,
                                              len(mus))):
                    i = mus[sub_idx]
                    j = nus[sub_idx]
                    x1 = x1_vec[i]
                    x2 = x2_vec[j]
                    if not np.all(x1 == x2):
                        to_be_computed_data_pair.append((x1, x2))
                        to_be_computed_index.append((i, j))

                if use_parameterized_circuits:
                    # build parameterized circuits, it could be slower for building circuit
                    # but overall it should be faster since it only transpile one circuit
                    feature_map_params_x = ParameterVector(
                        'x', feature_map.feature_dimension)
                    feature_map_params_y = ParameterVector(
                        'y', feature_map.feature_dimension)
                    parameterized_circuit = QSVM._construct_circuit(
                        (feature_map_params_x, feature_map_params_y),
                        feature_map,
                        measurement,
                        is_statevector_sim=is_statevector_sim)
                    parameterized_circuit = quantum_instance.transpile(
                        parameterized_circuit)[0]
                    circuits = [
                        parameterized_circuit.assign_parameters({
                            feature_map_params_x:
                            x,
                            feature_map_params_y:
                            y
                        }) for x, y in to_be_computed_data_pair
                    ]
                else:
                    if logger.isEnabledFor(logging.DEBUG):
                        logger.debug("Building circuits:")
                        TextProgressBar(sys.stderr)
                    circuits = parallel_map(
                        QSVM._construct_circuit,
                        to_be_computed_data_pair,
                        task_args=(feature_map, measurement),
                        num_processes=aqua_globals.num_processes)

                results = quantum_instance.execute(
                    circuits, had_transpiled=use_parameterized_circuits)

                if logger.isEnabledFor(logging.DEBUG):
                    logger.debug("Calculating overlap:")
                    TextProgressBar(sys.stderr)
                matrix_elements = parallel_map(
                    QSVM._compute_overlap,
                    range(len(circuits)),
                    task_args=(results, is_statevector_sim, measurement_basis),
                    num_processes=aqua_globals.num_processes)

                for (i, j), value in zip(to_be_computed_index,
                                         matrix_elements):
                    mat[i, j] = value
                    if is_symmetric:
                        mat[j, i] = mat[i, j]

        if enforce_psd and is_symmetric and not is_statevector_sim:
            # Find the closest positive semi-definite approximation to kernel matrix, in case it is
            # symmetric. The (symmetric) matrix should always be positive semi-definite by
            # construction, but this can be violated in case of noise, such as sampling noise, thus,
            # the adjustment is only done if NOT using the statevector simulation.
            D, U = np.linalg.eig(mat)
            mat = U @ np.diag(np.maximum(0, D)) @ U.transpose()

        return mat
 def pauli_block(self, pauli_string):
     """Get the Pauli block for the feature map circuit."""
     params = ParameterVector("_", length=len(pauli_string))
     time = self._data_map_func(np.asarray(params))
     return self.pauli_evolution(pauli_string, time)
Пример #16
0
    def __init__(
        self,
        feature_map: Union[QuantumCircuit, FeatureMap],
        training_dataset: Optional[Dict[str, np.ndarray]] = None,
        test_dataset: Optional[Dict[str, np.ndarray]] = None,
        datapoints: Optional[np.ndarray] = None,
        multiclass_extension: Optional[MulticlassExtension] = None,
        quantum_instance: Optional[Union[QuantumInstance, BaseBackend,
                                         Backend]] = None
    ) -> None:
        """
        Args:
            feature_map: Feature map module, used to transform data
            training_dataset: Training dataset.
            test_dataset: Testing dataset.
            datapoints: Prediction dataset.
            multiclass_extension: If number of classes is greater than 2 then a multiclass scheme
                must be supplied, in the form of a multiclass extension.
            quantum_instance: Quantum Instance or Backend

        Raises:
            AquaError: Multiclass extension not supplied when number of classes > 2
        """
        super().__init__(quantum_instance)
        # check the validity of provided arguments if possible
        if training_dataset is not None:
            is_multiclass = get_num_classes(training_dataset) > 2
            if is_multiclass:
                if multiclass_extension is None:
                    raise AquaError('Dataset has more than two classes. '
                                    'A multiclass extension must be provided.')
            else:
                if multiclass_extension is not None:
                    logger.warning(
                        "Dataset has just two classes. "
                        "Supplied multiclass extension will be ignored")

        self.training_dataset = None
        self.test_dataset = None
        self.datapoints = None
        self.class_to_label = None
        self.label_to_class = None
        self.num_classes = None

        self.setup_training_data(training_dataset)
        self.setup_test_data(test_dataset)
        self.setup_datapoint(datapoints)

        self.feature_map = feature_map
        self.num_qubits = self.feature_map.num_qubits

        if isinstance(feature_map, QuantumCircuit):
            # patch the feature dimension attribute to the circuit
            self.feature_map.feature_dimension = len(feature_map.parameters)
            if not hasattr(feature_map, 'ordered_parameters'):
                self.feature_map.ordered_parameters = sorted(
                    feature_map.parameters, key=lambda p: p.name)
            self.feature_map_params_x = ParameterVector(
                'x', self.feature_map.feature_dimension)
            self.feature_map_params_y = ParameterVector(
                'y', self.feature_map.feature_dimension)
        else:
            if not isinstance(feature_map, RawFeatureVector):
                warnings.warn("""
                The {} object as input for the QSVM is deprecated as of 0.7.0 and will
                be removed no earlier than 3 months after the release.
                You should pass a QuantumCircuit object instead.
                See also qiskit.circuit.library.data_preparation for a collection
                of suitable circuits.""".format(type(feature_map)),
                              DeprecationWarning,
                              stacklevel=2)
            self.feature_map_params_x = ParameterVector(
                'x', feature_map.feature_dimension)
            self.feature_map_params_y = ParameterVector(
                'y', feature_map.feature_dimension)

        qsvm_instance = None  # type: Optional[Union[_QSVM_Binary, _QSVM_Multiclass]]
        if multiclass_extension is None:
            qsvm_instance = _QSVM_Binary(self)
        else:
            multiclass_extension.set_estimator(_QSVM_Estimator, [feature_map])
            qsvm_instance = _QSVM_Multiclass(self, multiclass_extension)

        self.instance = qsvm_instance
Пример #17
0
    def __init__(
        self,
        num_qubits: Optional[int] = None,
        rotation_blocks: Optional[
            Union[QuantumCircuit, List[QuantumCircuit], Instruction, List[Instruction]]
        ] = None,
        entanglement_blocks: Optional[
            Union[QuantumCircuit, List[QuantumCircuit], Instruction, List[Instruction]]
        ] = None,
        entanglement: Optional[Union[List[int], List[List[int]]]] = None,
        reps: int = 1,
        insert_barriers: bool = False,
        parameter_prefix: str = "θ",
        overwrite_block_parameters: Union[bool, List[List[Parameter]]] = True,
        skip_final_rotation_layer: bool = False,
        skip_unentangled_qubits: bool = False,
        initial_state: Optional[Any] = None,
        name: Optional[str] = "nlocal",
    ) -> None:
        """Create a new n-local circuit.

        Args:
            num_qubits: The number of qubits of the circuit.
            rotation_blocks: The blocks used in the rotation layers. If multiple are passed,
                these will be applied one after another (like new sub-layers).
            entanglement_blocks: The blocks used in the entanglement layers. If multiple are passed,
                these will be applied one after another. To use different entanglements for
                the sub-layers, see :meth:`get_entangler_map`.
            entanglement: The indices specifying on which qubits the input blocks act. If None, the
                entanglement blocks are applied at the top of the circuit.
            reps: Specifies how often the rotation blocks and entanglement blocks are repeated.
            insert_barriers: If True, barriers are inserted in between each layer. If False,
                no barriers are inserted.
            parameter_prefix: The prefix used if default parameters are generated.
            overwrite_block_parameters: If the parameters in the added blocks should be overwritten.
                If False, the parameters in the blocks are not changed.
            skip_final_rotation_layer: Whether a final rotation layer is added to the circuit.
            skip_unentangled_qubits: If ``True``, the rotation gates act only on qubits that
                are entangled. If ``False``, the rotation gates act on all qubits.
            initial_state: A `QuantumCircuit` object which can be used to describe an initial state
                prepended to the NLocal circuit.
            name: The name of the circuit.

        Examples:
            TODO

        Raises:
            ImportError: If an ``initial_state`` is specified but Qiskit Aqua is not installed.
            ValueError: If reps parameter is less than or equal to 0.
            TypeError: If reps parameter is not an int value.
        """
        super().__init__(name=name)

        self._num_qubits = None
        self._insert_barriers = insert_barriers
        self._reps = reps
        self._entanglement_blocks = []
        self._rotation_blocks = []
        self._prepended_blocks = []
        self._prepended_entanglement = []
        self._appended_blocks = []
        self._appended_entanglement = []
        self._entanglement = None
        self._entangler_maps = None
        self._ordered_parameters = ParameterVector(name=parameter_prefix)
        self._overwrite_block_parameters = overwrite_block_parameters
        self._skip_final_rotation_layer = skip_final_rotation_layer
        self._skip_unentangled_qubits = skip_unentangled_qubits
        self._initial_state, self._initial_state_circuit = None, None
        self._bounds = None

        if int(reps) != reps:
            raise TypeError("The value of reps should be int")

        if reps < 0:
            raise ValueError("The value of reps should be larger than or equal to 0")

        if num_qubits is not None:
            self.num_qubits = num_qubits

        if entanglement_blocks is not None:
            self.entanglement_blocks = entanglement_blocks

        if rotation_blocks is not None:
            self.rotation_blocks = rotation_blocks

        if entanglement is not None:
            self.entanglement = entanglement

        if initial_state is not None:
            self.initial_state = initial_state
Пример #18
0
    def get_loss(
        self,
        hamiltonian: OperatorBase,
        ansatz: QuantumCircuit,
        dt: float,
        current_parameters: np.ndarray,
    ) -> Tuple[Callable[[np.ndarray], float], Optional[Callable[[np.ndarray], np.ndarray]]]:

        """Get a function to evaluate the infidelity between Trotter step and ansatz.

        Args:
            hamiltonian: The Hamiltonian under which to evolve.
            ansatz: The parameterized quantum circuit which attempts to approximate the
                time-evolved state.
            dt: The time step.
            current_parameters: The current parameters.

        Returns:
            A callable to evaluate the infidelity and, if gradients are supported and required,
                a second callable to evaluate the gradient of the infidelity.
        """
        self._validate_setup(skip={"optimizer"})

        # use Trotterization to evolve the current state
        trotterized = ansatz.bind_parameters(current_parameters)

        if isinstance(hamiltonian, MatrixOp):
            evolution_gate = HamiltonianGate(hamiltonian.primitive, time=dt)
        else:
            evolution_gate = PauliEvolutionGate(hamiltonian, time=dt, synthesis=self.evolution)

        trotterized.append(evolution_gate, ansatz.qubits)

        # define the overlap of the Trotterized state and the ansatz
        x = ParameterVector("w", ansatz.num_parameters)
        shifted = ansatz.assign_parameters(current_parameters + x)
        overlap = StateFn(trotterized).adjoint() @ StateFn(shifted)

        converted = self.expectation.convert(overlap)

        def evaluate_loss(
            displacement: Union[np.ndarray, List[np.ndarray]]
        ) -> Union[float, List[float]]:
            """Evaluate the overlap of the ansatz with the Trotterized evolution.

            Args:
                displacement: The parameters for the ansatz.

            Returns:
                The fidelity of the ansatz with parameters ``theta`` and the Trotterized evolution.
            """
            if isinstance(displacement, list):
                displacement = np.asarray(displacement)
                value_dict = {x_i: displacement[:, i].tolist() for i, x_i in enumerate(x)}
            else:
                value_dict = dict(zip(x, displacement))

            sampled = self._sampler.convert(converted, params=value_dict)

            # in principle we could add different loss functions here, but we're currently
            # not aware of a use-case for a different one than in the paper
            return 1 - np.abs(sampled.eval()) ** 2

        if _is_gradient_supported(ansatz) and self.use_parameter_shift:

            def evaluate_gradient(displacement: np.ndarray) -> np.ndarray:
                """Evaluate the gradient with the parameter-shift rule.

                This is hardcoded here since the gradient framework does not support computing
                gradients for overlaps.

                Args:
                    displacement: The parameters for the ansatz.

                Returns:
                    The gradient.
                """
                # construct lists where each element is shifted by plus (or minus) pi/2
                dim = displacement.size
                plus_shifts = (displacement + np.pi / 2 * np.identity(dim)).tolist()
                minus_shifts = (displacement - np.pi / 2 * np.identity(dim)).tolist()

                evaluated = evaluate_loss(plus_shifts + minus_shifts)

                gradient = (evaluated[:dim] - evaluated[dim:]) / 2

                return gradient

        else:
            evaluate_gradient = None

        return evaluate_loss, evaluate_gradient
Пример #19
0
    def __init__(
            self,
            optimizer=None,
            feature_map=None,
            var_form=None,
            training_dataset=None,
            test_dataset=None,
            datapoints=None,
            max_evals_grouped=1,
            minibatch_size=-1,
            callback=None
    ):
        """Initialize the object
        Args:
            optimizer (Optimizer): The classical optimizer to use.
            feature_map (FeatureMap): The FeatureMap instance to use.
            var_form (VariationalForm): The variational form instance.
            training_dataset (dict): The training dataset, in the format:
                                    {'A': np.ndarray, 'B': np.ndarray, ...}.
            test_dataset (dict): The test dataset, in same format as `training_dataset`.
            datapoints (np.ndarray): NxD array, N is the number of data and D is data dimension.
            max_evals_grouped (int): The maximum number of evaluations to perform simultaneously.
            minibatch_size (int): The size of a mini-batch.
            callback (Callable): a callback that can access the
                intermediate data during the optimization.
                Internally, four arguments are provided as follows the index
                of data batch, the index of evaluation,
                parameters of variational form, evaluated value.
        Notes:
            We use `label` to denotes numeric results and `class` the class names (str).
        Raises:
            AquaError: invalid input
        """

        self.validate(locals())
        super().__init__(
            var_form=var_form,
            optimizer=optimizer,
            cost_fn=self._cost_function_wrapper
        )
        self._batches = None
        self._label_batches = None
        self._batch_index = None
        self._eval_time = None
        self.batch_num = None
        self._optimizer.set_max_evals_grouped(max_evals_grouped)

        self._callback = callback

        if feature_map is None:
            raise AquaError('Missing feature map.')
        if training_dataset is None:
            raise AquaError('Missing training dataset.')
        self._training_dataset, self._class_to_label = split_dataset_to_data_and_labels(
            training_dataset)
        self._label_to_class = {label: class_name for class_name, label
                                in self._class_to_label.items()}
        self._num_classes = len(list(self._class_to_label.keys()))

        if test_dataset is not None:
            self._test_dataset = split_dataset_to_data_and_labels(test_dataset,
                                                                  self._class_to_label)
        else:
            self._test_dataset = test_dataset

        if datapoints is not None and not isinstance(datapoints, np.ndarray):
            datapoints = np.asarray(datapoints)
            if len(datapoints) == 0:  # pylint: disable=len-as-condition
                datapoints = None
        self._datapoints = datapoints
        self._minibatch_size = minibatch_size

        self._eval_count = 0
        self._ret = {}
        self._feature_map = feature_map
        self._num_qubits = feature_map.num_qubits
        self._var_form_params = ParameterVector('θ', self._var_form.num_parameters)
        self._feature_map_params = ParameterVector('x', self._feature_map.feature_dimension)
        self._parameterized_circuits = None
Пример #20
0
def _compose_transforms(basis_transforms, source_basis, source_dag):
    """Compose a set of basis transforms into a set of replacements.

    Args:
        basis_transforms (List[Tuple[gate_name, params, equiv]]): List of
            transforms to compose.
        source_basis (Set[Tuple[gate_name: str, gate_num_qubits: int]]): Names
            of gates which need to be translated.
        source_dag (DAGCircuit): DAG with example gates from source_basis.
            (Used to determine num_params for gate in source_basis.)

    Returns:
        Dict[gate_name, Tuple(params, dag)]: Dictionary mapping between each gate
            in source_basis and a DAGCircuit instance to replace it. Gates in
            source_basis but not affected by basis_transforms will be included
            as a key mapping to itself.
    """

    example_gates = {(node.op.name, node.op.num_qubits): node.op
                     for node in source_dag.op_nodes()}
    mapped_instrs = {}

    for gate_name, gate_num_qubits in source_basis:
        # Need to grab a gate instance to find num_qubits and num_params.
        # Can be removed following https://github.com/Qiskit/qiskit-terra/pull/3947 .
        example_gate = example_gates[gate_name, gate_num_qubits]
        num_params = len(example_gate.params)

        placeholder_params = ParameterVector(gate_name, num_params)
        placeholder_gate = Gate(gate_name, gate_num_qubits,
                                list(placeholder_params))
        placeholder_gate.params = list(placeholder_params)

        dag = DAGCircuit()
        qr = QuantumRegister(gate_num_qubits)
        dag.add_qreg(qr)
        dag.apply_operation_back(placeholder_gate, qr[:], [])
        mapped_instrs[gate_name, gate_num_qubits] = placeholder_params, dag

    for gate_name, gate_num_qubits, equiv_params, equiv in basis_transforms:
        logger.debug('Composing transform step: %s/%s %s =>\n%s', gate_name,
                     gate_num_qubits, equiv_params, equiv)

        for mapped_instr_name, (dag_params, dag) in mapped_instrs.items():
            doomed_nodes = [
                node for node in dag.op_nodes()
                if (node.op.name, node.op.num_qubits) == (gate_name,
                                                          gate_num_qubits)
            ]

            if doomed_nodes and logger.isEnabledFor(logging.DEBUG):
                from qiskit.converters import dag_to_circuit
                logger.debug(
                    'Updating transform for mapped instr %s %s from \n%s',
                    mapped_instr_name, dag_params, dag_to_circuit(dag))

            for node in doomed_nodes:
                from qiskit.converters import circuit_to_dag

                replacement = equiv.assign_parameters(
                    dict(zip_longest(equiv_params, node.op.params)))

                replacement_dag = circuit_to_dag(replacement)

                dag.substitute_node_with_dag(node, replacement_dag)

            if doomed_nodes and logger.isEnabledFor(logging.DEBUG):
                from qiskit.converters import dag_to_circuit
                logger.debug('Updated transform for mapped instr %s %s to\n%s',
                             mapped_instr_name, dag_params,
                             dag_to_circuit(dag))

    return mapped_instrs
Пример #21
0
entangler_map = []
for i in range(sum(num_qubits)):
    entangler_map.append([i, int(np.mod(i+1, sum(num_qubits)))])

# Load the trained circuit parameters
g_params = [0.29399714, 0.38853322, 0.9557694, 0.07245791, 6.02626428, 0.13537225]

# Set an initial state for the generator circuit
init_dist = NormalDistribution(sum(num_qubits), mu=1., sigma=1., low=bounds[0], high=bounds[1])
init_distribution = np.sqrt(init_dist.probabilities)
init_distribution = Custom(num_qubits=sum(num_qubits), state_vector=init_distribution)

# construct the variational form
var_form = RealAmplitudes(sum(num_qubits), entanglement=entangler_map, reps=1, initial_state=init_distribution)
var_form.entanglement_blocks = 'cz'
theta = ParameterVector('θ', var_form.num_parameters)
var_form = var_form.assign_parameters(theta)

# Set generator circuit
g_circuit = UnivariateVariationalDistribution(sum(num_qubits), var_form, g_params,
                                              low=bounds[0], high=bounds[1])
g_circuit._var_form_params = theta

# construct circuit factory for uncertainty model
uncertainty_model = g_circuit


# set the strike price (should be within the low and the high value of the uncertainty)
strike_price = 2

# set the approximation scaling for the payoff function