Beispiel #1
0
    def test_ecev(self):
        """ European Call Expected Value test """
        bounds = np.array([0., 7.])
        num_qubits = [3]
        entangler_map = []
        for i in range(sum(num_qubits)):
            entangler_map.append([i, int(np.mod(i + 1, sum(num_qubits)))])

        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(int(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)
        var_form = TwoLocal(int(np.sum(num_qubits)),
                            'ry',
                            'cz',
                            reps=1,
                            initial_state=init_distribution,
                            entanglement=entangler_map)

        uncertainty_model = UnivariateVariationalDistribution(int(
            sum(num_qubits)),
                                                              var_form,
                                                              g_params,
                                                              low=bounds[0],
                                                              high=bounds[1])

        strike_price = 2
        c_approx = 0.25
        european_call = EuropeanCallExpectedValue(uncertainty_model,
                                                  strike_price=strike_price,
                                                  c_approx=c_approx)

        uncertainty_model.set_probabilities(
            QuantumInstance(BasicAer.get_backend('statevector_simulator')))

        algo = AmplitudeEstimation(5, european_call)
        result = algo.run(
            quantum_instance=BasicAer.get_backend('statevector_simulator'))
        self.assertAlmostEqual(result['estimation'], 1.2580, places=4)
        self.assertAlmostEqual(result['max_probability'], 0.8785, places=4)
Beispiel #2
0
    def setUp(self):
        super().setUp()

        self.seed = 7
        aqua_globals.random_seed = self.seed
        # Number training data samples
        n_v = 5000
        # Load data samples from log-normal distribution with mean=1 and standard deviation=1
        m_u = 1
        sigma = 1
        self._real_data = aqua_globals.random.lognormal(mean=m_u, sigma=sigma, size=n_v)
        # Set upper and lower data values as list of k
        # min/max data values [[min_0,max_0],...,[min_k-1,max_k-1]]
        self._bounds = [0., 3.]
        # Set number of qubits per data dimension as list of k qubit values[#q_0,...,#q_k-1]
        num_qubits = [2]
        # Batch size
        batch_size = 100
        # Set number of training epochs
        # num_epochs = 10
        num_epochs = 5

        # Initialize qGAN
        self.qgan = QGAN(self._real_data,
                         self._bounds,
                         num_qubits,
                         batch_size,
                         num_epochs,
                         snapshot_dir=None)
        self.qgan.seed = 7
        # Set quantum instance to run the quantum generator
        self.qi_statevector = QuantumInstance(backend=BasicAer.get_backend('statevector_simulator'),
                                              seed_simulator=2,
                                              seed_transpiler=2)
        self.qi_qasm = QuantumInstance(backend=BasicAer.get_backend('qasm_simulator'),
                                       shots=1000,
                                       seed_simulator=2,
                                       seed_transpiler=2)
        # Set entangler map
        entangler_map = [[0, 1]]

        # Set an initial state for the generator circuit
        init_dist = UniformDistribution(sum(num_qubits), low=self._bounds[0], high=self._bounds[1])
        q = QuantumRegister(sum(num_qubits), name='q')
        qc = QuantumCircuit(q)
        init_dist.build(qc, q)
        init_distribution = Custom(num_qubits=sum(num_qubits), circuit=qc)

        # Set generator's initial parameters
        init_params = aqua_globals.random.random(2 * sum(num_qubits)) * 2 * 1e-2

        # Set variational form
        var_form = RealAmplitudes(sum(num_qubits), reps=1, initial_state=init_distribution,
                                  entanglement=entangler_map)
        self.generator_circuit = UnivariateVariationalDistribution(sum(num_qubits), var_form,
                                                                   init_params,
                                                                   low=self._bounds[0],
                                                                   high=self._bounds[1])
Beispiel #3
0
    def __init__(self,
                 bounds: np.ndarray,
                 num_qubits: List[int],
                 generator_circuit: Optional[
                     Union[UnivariateVariationalDistribution,
                           MultivariateVariationalDistribution,
                           QuantumCircuit]] = None,
                 init_params: Optional[Union[List[float], np.ndarray]] = None,
                 snapshot_dir: Optional[str] = None) -> None:
        """
        Args:
            bounds: k min/max data values [[min_1,max_1],...,[min_k,max_k]],
                given input data dim k
            num_qubits: k numbers of qubits to determine representation resolution,
                i.e. n qubits enable the representation of 2**n values [n_1,..., n_k]
            generator_circuit: a UnivariateVariationalDistribution for univariate data,
                a MultivariateVariationalDistribution for multivariate data,
                or a QuantumCircuit implementing the generator.
            init_params: 1D numpy array or list, Initialization for
                the generator's parameters.
            snapshot_dir: str or None, if not None save the optimizer's parameter after every
                update step to the given directory

        Raises:
            AquaError: Set multivariate variational distribution to represent multivariate data
        """
        super().__init__()
        self._bounds = bounds
        self._num_qubits = num_qubits
        self.generator_circuit = generator_circuit
        if self.generator_circuit is None:
            entangler_map = []
            if np.sum(num_qubits) > 2:
                for i in range(int(np.sum(num_qubits))):
                    entangler_map.append(
                        [i, int(np.mod(i + 1, np.sum(num_qubits)))])
            else:
                if np.sum(num_qubits) > 1:
                    entangler_map.append([0, 1])

            if len(num_qubits) > 1:
                num_qubits = list(map(int, num_qubits))
                low = bounds[:, 0].tolist()
                high = bounds[:, 1].tolist()
                init_dist = MultivariateUniformDistribution(num_qubits,
                                                            low=low,
                                                            high=high)
                q = QuantumRegister(sum(num_qubits))
                qc = QuantumCircuit(q)
                init_dist.build(qc, q)
                init_distribution = Custom(num_qubits=sum(num_qubits),
                                           circuit=qc)
                # Set variational form
                var_form = RY(sum(num_qubits),
                              depth=1,
                              initial_state=init_distribution,
                              entangler_map=entangler_map,
                              entanglement_gate='cz')
                if init_params is None:
                    init_params = aqua_globals.random.rand(
                        var_form.num_parameters) * 2 * 1e-2
                # Set generator circuit
                self.generator_circuit = MultivariateVariationalDistribution(
                    num_qubits, var_form, init_params, low=low, high=high)
            else:
                init_dist = UniformDistribution(sum(num_qubits),
                                                low=bounds[0],
                                                high=bounds[1])
                q = QuantumRegister(sum(num_qubits), name='q')
                qc = QuantumCircuit(q)
                init_dist.build(qc, q)
                init_distribution = Custom(num_qubits=sum(num_qubits),
                                           circuit=qc)
                var_form = RY(sum(num_qubits),
                              depth=1,
                              initial_state=init_distribution,
                              entangler_map=entangler_map,
                              entanglement_gate='cz')
                if init_params is None:
                    init_params = aqua_globals.random.rand(
                        var_form.num_parameters) * 2 * 1e-2
                # Set generator circuit
                self.generator_circuit = UnivariateVariationalDistribution(
                    int(np.sum(num_qubits)),
                    var_form,
                    init_params,
                    low=bounds[0],
                    high=bounds[1])

        if len(num_qubits) > 1:
            if isinstance(self.generator_circuit,
                          MultivariateVariationalDistribution):
                pass
            else:
                raise AquaError('Set multivariate variational distribution '
                                'to represent multivariate data')
        else:
            if isinstance(self.generator_circuit,
                          UnivariateVariationalDistribution):
                pass
            else:
                raise AquaError('Set univariate variational distribution '
                                'to represent univariate data')
        # Set optimizer for updating the generator network
        self._optimizer = ADAM(maxiter=1,
                               tol=1e-6,
                               lr=1e-3,
                               beta_1=0.7,
                               beta_2=0.99,
                               noise_factor=1e-6,
                               eps=1e-6,
                               amsgrad=True,
                               snapshot_dir=snapshot_dir)

        if np.ndim(self._bounds) == 1:
            bounds = np.reshape(self._bounds, (1, len(self._bounds)))
        else:
            bounds = self._bounds
        for j, prec in enumerate(self._num_qubits):
            # prepare data grid for dim j
            grid = np.linspace(bounds[j, 0], bounds[j, 1], (2**prec))
            if j == 0:
                if len(self._num_qubits) > 1:
                    self._data_grid = [grid]
                else:
                    self._data_grid = grid
                self._grid_elements = grid
            elif j == 1:
                self._data_grid.append(grid)
                temp = []
                for g_e in self._grid_elements:
                    for g in grid:
                        temp0 = [g_e]
                        temp0.append(g)
                        temp.append(temp0)
                self._grid_elements = temp
            else:
                self._data_grid.append(grid)
                temp = []
                for g_e in self._grid_elements:
                    for g in grid:
                        temp0 = deepcopy(g_e)
                        temp0.append(g)
                        temp.append(temp0)
                self._grid_elements = deepcopy(temp)
        self._data_grid = np.array(self._data_grid)

        self._shots = None
        self._discriminator = None
        self._ret = {}
Beispiel #4
0
class QuantumGenerator(GenerativeNetwork):
    """
    Quantum Generator.

    The quantum generator is a parametrized quantum circuit which can be trained with the
    :class:`~qiskit.aqua.algorithms.QGAN` algorithm
    to generate a quantum state which approximates the probability
    distribution of given training data. At the beginning of the training the parameters will
    be set randomly, thus, the output will is random. Throughout the training the quantum
    generator learns to represent the target distribution.
    Eventually, the trained generator can be used for state preparation e.g. in QAE.
    """
    def __init__(self,
                 bounds: np.ndarray,
                 num_qubits: List[int],
                 generator_circuit: Optional[
                     Union[UnivariateVariationalDistribution,
                           MultivariateVariationalDistribution,
                           QuantumCircuit]] = None,
                 init_params: Optional[Union[List[float], np.ndarray]] = None,
                 snapshot_dir: Optional[str] = None) -> None:
        """
        Args:
            bounds: k min/max data values [[min_1,max_1],...,[min_k,max_k]],
                given input data dim k
            num_qubits: k numbers of qubits to determine representation resolution,
                i.e. n qubits enable the representation of 2**n values [n_1,..., n_k]
            generator_circuit: a UnivariateVariationalDistribution for univariate data,
                a MultivariateVariationalDistribution for multivariate data,
                or a QuantumCircuit implementing the generator.
            init_params: 1D numpy array or list, Initialization for
                the generator's parameters.
            snapshot_dir: str or None, if not None save the optimizer's parameter after every
                update step to the given directory

        Raises:
            AquaError: Set multivariate variational distribution to represent multivariate data
        """
        super().__init__()
        self._bounds = bounds
        self._num_qubits = num_qubits
        self.generator_circuit = generator_circuit
        if self.generator_circuit is None:
            entangler_map = []
            if np.sum(num_qubits) > 2:
                for i in range(int(np.sum(num_qubits))):
                    entangler_map.append(
                        [i, int(np.mod(i + 1, np.sum(num_qubits)))])
            else:
                if np.sum(num_qubits) > 1:
                    entangler_map.append([0, 1])

            if len(num_qubits) > 1:
                num_qubits = list(map(int, num_qubits))
                low = bounds[:, 0].tolist()
                high = bounds[:, 1].tolist()
                init_dist = MultivariateUniformDistribution(num_qubits,
                                                            low=low,
                                                            high=high)
                q = QuantumRegister(sum(num_qubits))
                qc = QuantumCircuit(q)
                init_dist.build(qc, q)
                init_distribution = Custom(num_qubits=sum(num_qubits),
                                           circuit=qc)
                # Set variational form
                var_form = RY(sum(num_qubits),
                              depth=1,
                              initial_state=init_distribution,
                              entangler_map=entangler_map,
                              entanglement_gate='cz')
                if init_params is None:
                    init_params = aqua_globals.random.rand(
                        var_form.num_parameters) * 2 * 1e-2
                # Set generator circuit
                self.generator_circuit = MultivariateVariationalDistribution(
                    num_qubits, var_form, init_params, low=low, high=high)
            else:
                init_dist = UniformDistribution(sum(num_qubits),
                                                low=bounds[0],
                                                high=bounds[1])
                q = QuantumRegister(sum(num_qubits), name='q')
                qc = QuantumCircuit(q)
                init_dist.build(qc, q)
                init_distribution = Custom(num_qubits=sum(num_qubits),
                                           circuit=qc)
                var_form = RY(sum(num_qubits),
                              depth=1,
                              initial_state=init_distribution,
                              entangler_map=entangler_map,
                              entanglement_gate='cz')
                if init_params is None:
                    init_params = aqua_globals.random.rand(
                        var_form.num_parameters) * 2 * 1e-2
                # Set generator circuit
                self.generator_circuit = UnivariateVariationalDistribution(
                    int(np.sum(num_qubits)),
                    var_form,
                    init_params,
                    low=bounds[0],
                    high=bounds[1])

        if len(num_qubits) > 1:
            if isinstance(self.generator_circuit,
                          MultivariateVariationalDistribution):
                pass
            else:
                raise AquaError('Set multivariate variational distribution '
                                'to represent multivariate data')
        else:
            if isinstance(self.generator_circuit,
                          UnivariateVariationalDistribution):
                pass
            else:
                raise AquaError('Set univariate variational distribution '
                                'to represent univariate data')
        # Set optimizer for updating the generator network
        self._optimizer = ADAM(maxiter=1,
                               tol=1e-6,
                               lr=1e-3,
                               beta_1=0.7,
                               beta_2=0.99,
                               noise_factor=1e-6,
                               eps=1e-6,
                               amsgrad=True,
                               snapshot_dir=snapshot_dir)

        if np.ndim(self._bounds) == 1:
            bounds = np.reshape(self._bounds, (1, len(self._bounds)))
        else:
            bounds = self._bounds
        for j, prec in enumerate(self._num_qubits):
            # prepare data grid for dim j
            grid = np.linspace(bounds[j, 0], bounds[j, 1], (2**prec))
            if j == 0:
                if len(self._num_qubits) > 1:
                    self._data_grid = [grid]
                else:
                    self._data_grid = grid
                self._grid_elements = grid
            elif j == 1:
                self._data_grid.append(grid)
                temp = []
                for g_e in self._grid_elements:
                    for g in grid:
                        temp0 = [g_e]
                        temp0.append(g)
                        temp.append(temp0)
                self._grid_elements = temp
            else:
                self._data_grid.append(grid)
                temp = []
                for g_e in self._grid_elements:
                    for g in grid:
                        temp0 = deepcopy(g_e)
                        temp0.append(g)
                        temp.append(temp0)
                self._grid_elements = deepcopy(temp)
        self._data_grid = np.array(self._data_grid)

        self._shots = None
        self._discriminator = None
        self._ret = {}

    def set_seed(self, seed):
        """
        Set seed.

        Args:
            seed (int): seed
        """
        aqua_globals.random_seed = seed

    def set_discriminator(self, discriminator):
        """
        Set discriminator network.

        Args:
            discriminator (Discriminator): Discriminator used to compute the loss function.
        """
        self._discriminator = discriminator

    def construct_circuit(self, params=None):
        """
        Construct generator circuit.

        Args:
            params (numpy.ndarray): parameters which should be used to run the generator,
                    if None use self._params

        Returns:
            Instruction: construct the quantum circuit and return as gate
        """

        q = QuantumRegister(sum(self._num_qubits), name='q')
        qc = QuantumCircuit(q)
        if params is None:
            self.generator_circuit.build(qc=qc, q=q)
        else:
            generator_circuit_copy = deepcopy(self.generator_circuit)
            generator_circuit_copy.params = params
            generator_circuit_copy.build(qc=qc, q=q)

        # return qc.copy(name='qc')
        return qc.to_instruction()

    def get_output(self,
                   quantum_instance,
                   qc_state_in=None,
                   params=None,
                   shots=None):
        """
        Get classical data samples from the generator.
        Running the quantum generator circuit results in a quantum state.
        To train this generator with a classical discriminator, we need to sample classical outputs
        by measuring the quantum state and mapping them to feature space defined by the training
        data.

        Args:
            quantum_instance (QuantumInstance): Quantum Instance, used to run the generator
                circuit.
            qc_state_in (QuantumCircuit): deprecated
            params (numpy.ndarray): array or None, parameters which should
                be used to run the generator, if None use self._params
            shots (int): if not None use a number of shots that is different from the
                number set in quantum_instance

        Returns:
            list: generated samples, array: sample occurrence in percentage
        """
        instance_shots = quantum_instance.run_config.shots
        q = QuantumRegister(sum(self._num_qubits), name='q')
        qc = QuantumCircuit(q)
        qc.append(self.construct_circuit(params), q)
        if quantum_instance.is_statevector:
            pass
        else:
            c = ClassicalRegister(sum(self._num_qubits), name='c')
            qc.add_register(c)
            qc.measure(q, c)

        if shots is not None:
            quantum_instance.set_config(shots=shots)

        result = quantum_instance.execute(qc)

        generated_samples = []
        if quantum_instance.is_statevector:
            result = result.get_statevector(qc)
            values = np.multiply(result, np.conj(result))
            values = list(values.real)
            keys = []
            for j in range(len(values)):
                keys.append(np.binary_repr(j, int(sum(self._num_qubits))))
        else:
            result = result.get_counts(qc)
            keys = list(result)
            values = list(result.values())
            values = [float(v) / np.sum(values) for v in values]
        generated_samples_weights = values
        for i, _ in enumerate(keys):
            index = 0
            temp = []
            for k, p in enumerate(self._num_qubits):
                bin_rep = 0
                j = 0
                while j < p:
                    bin_rep += int(keys[i][index]) * 2**(int(p) - j - 1)
                    j += 1
                    index += 1
                if len(self._num_qubits) > 1:
                    temp.append(self._data_grid[k][int(bin_rep)])
                else:
                    temp.append(self._data_grid[int(bin_rep)])
            generated_samples.append(temp)

        self.generator_circuit._probabilities = generated_samples_weights
        if shots is not None:
            # Restore the initial quantum_instance configuration
            quantum_instance.set_config(shots=instance_shots)
        return generated_samples, generated_samples_weights

    def loss(self, x, weights):  # pylint: disable=arguments-differ
        """
        Loss function for training the generator's parameters.

        Args:
            x (numpy.ndarray): sample label (equivalent to discriminator output)
            weights (numpy.ndarray): probability for measuring the sample

        Returns:
            float: loss function
        """
        try:
            # pylint: disable=no-member
            loss = (-1) * np.dot(np.log(x).transpose(), weights)
        except Exception:  # pylint: disable=broad-except
            loss = (-1) * np.dot(np.log(x), weights)
        return loss.flatten()

    def _get_objective_function(self, quantum_instance, discriminator):
        """
        Get objective function

        Args:
            quantum_instance (QuantumInstance): used to run the quantum circuit.
            discriminator (torch.nn.Module): discriminator network to compute the sample labels.

        Returns:
            objective_function: objective function for quantum generator optimization
        """
        def objective_function(params):
            """
            Objective function

            Args:
                params (numpy.ndarray): generator parameters

            Returns:
                self.loss: loss function
            """
            generated_data, generated_prob = self.get_output(quantum_instance,
                                                             params=params,
                                                             shots=self._shots)
            prediction_generated = discriminator.get_label(generated_data,
                                                           detach=True)
            return self.loss(prediction_generated, generated_prob)

        return objective_function

    def train(self, quantum_instance=None, shots=None):
        """
        Perform one training step w.r.t to the generator's parameters

        Args:
            quantum_instance (QuantumInstance): used to run the generator circuit.
            shots (int): Number of shots for hardware or qasm execution.

        Returns:
            dict: generator loss(float) and updated parameters (array).
        """

        self._shots = shots
        # Force single optimization iteration
        self._optimizer._maxiter = 1
        self._optimizer._t = 0
        objective = self._get_objective_function(quantum_instance,
                                                 self._discriminator)
        self.generator_circuit.params, loss, _ = \
            self._optimizer.optimize(num_vars=len(self.generator_circuit.params),
                                     objective_function=objective,
                                     initial_point=self.generator_circuit.params)

        self._ret['loss'] = loss
        self._ret['params'] = self.generator_circuit.params

        return self._ret
Beispiel #5
0
    def setUp(self):
        super().setUp()

        # Number training data samples
        N = 5000
        # Load data samples from log-normal distribution with mean=1 and standard deviation=1
        mu = 1
        sigma = 1
        self._real_data = np.random.lognormal(mean=mu, sigma=sigma, size=N)
        # Set the data resolution
        # Set upper and lower data values as list of k min/max data values [[min_0,max_0],...,[min_k-1,max_k-1]]
        self._bounds = [0., 3.]
        # Set number of qubits per data dimension as list of k qubit values[#q_0,...,#q_k-1]
        num_qubits = [2]
        # Batch size
        batch_size = 100
        # Set number of training epochs
        num_epochs = 10
        self._params_torch = {
            'algorithm': {
                'name': 'QGAN',
                'num_qubits': num_qubits,
                'batch_size': batch_size,
                'num_epochs': num_epochs
            },
            'problem': {
                'name': 'distribution_learning_loading',
                'random_seed': 7
            },
            'generative_network': {
                'name': 'QuantumGenerator',
                'bounds': self._bounds,
                'num_qubits': num_qubits,
                'init_params': None,
                'snapshot_dir': None
            },
            'discriminative_network': {
                'name': 'PytorchDiscriminator',
                'n_features': len(num_qubits)
            }
        }
        self._params_numpy = {
            'algorithm': {
                'name': 'QGAN',
                'num_qubits': num_qubits,
                'batch_size': batch_size,
                'num_epochs': num_epochs
            },
            'problem': {
                'name': 'distribution_learning_loading',
                'random_seed': 7
            },
            'generative_network': {
                'name': 'QuantumGenerator',
                'bounds': self._bounds,
                'num_qubits': num_qubits,
                'init_params': None,
                'snapshot_dir': None
            },
            'discriminative_network': {
                'name': 'NumpyDiscriminator',
                'n_features': len(num_qubits)
            }
        }

        # Initialize qGAN
        self.qgan = QGAN(self._real_data,
                         self._bounds,
                         num_qubits,
                         batch_size,
                         num_epochs,
                         snapshot_dir=None)
        self.qgan.seed = 7
        # Set quantum instance to run the quantum generator
        self.quantum_instance_statevector = QuantumInstance(
            backend=BasicAer.get_backend('statevector_simulator'),
            circuit_caching=False,
            seed_simulator=2,
            seed_transpiler=2)
        self.quantum_instance_qasm = QuantumInstance(
            backend=BasicAer.get_backend('qasm_simulator'),
            shots=1000,
            circuit_caching=False,
            seed_simulator=2,
            seed_transpiler=2)
        # Set entangler map
        entangler_map = [[0, 1]]

        # Set an initial state for the generator circuit
        init_dist = UniformDistribution(sum(num_qubits),
                                        low=self._bounds[0],
                                        high=self._bounds[1])
        q = QuantumRegister(sum(num_qubits), name='q')
        qc = QuantumCircuit(q)
        init_dist.build(qc, q)
        init_distribution = Custom(num_qubits=sum(num_qubits), circuit=qc)
        # Set variational form
        var_form = RY(sum(num_qubits),
                      depth=1,
                      initial_state=init_distribution,
                      entangler_map=entangler_map,
                      entanglement_gate='cz')
        # Set generator's initial parameters
        init_params = aqua_globals.random.rand(
            var_form._num_parameters) * 2 * 1e-2
        # Set generator circuit
        g_circuit = UnivariateVariationalDistribution(sum(num_qubits),
                                                      var_form,
                                                      init_params,
                                                      low=self._bounds[0],
                                                      high=self._bounds[1])
        # initial_distribution=init_distribution,
        # Set quantum generator
        self.qgan.set_generator(generator_circuit=g_circuit)
Beispiel #6
0
quantum_instance = QuantumInstance(
    backend=BasicAer.get_backend('statevector_simulator'))
print("quantum_instance set")

# Set entangler map
entangler_map = [[0, 1]]


# Set an initial state for the generator circuit
init_dist = UniformDistribution(sum(num_qubits), low=bounds[0], high=bounds[1])
q = QuantumRegister(sum(num_qubits), name='q')
qc = QuantumCircuit(q)
init_dist.build(qc, q)
init_distribution = Custom(num_qubits=sum(num_qubits), circuit=qc)
var_form = TwoLocal(int(np.sum(num_qubits)), 'ry', 'cz', entanglement=entangler_map,
                    reps=1, initial_state=init_distribution)
# Set generator's initial parameters
init_params = aqua_globals.random.rand(
    var_form.num_parameters_settable) * 2 * np.pi
# Set generator circuit
g_circuit = UnivariateVariationalDistribution(int(sum(num_qubits)), var_form, init_params,
                                              low=bounds[0], high=bounds[1])

print("g_circuit set")

# Set quantum generator
qgan.set_generator(generator_circuit=g_circuit)
# Set classical discriminator neural network
discriminator = NumPyDiscriminator(len(num_qubits))
qgan.set_discriminator(discriminator)
class QuantumGenerator(GenerativeNetwork):
    """
    Generator
    """
    CONFIGURATION = {
        'name': 'QuantumGenerator',
        'description': 'qGAN Generator Network',
        'input_schema': {
            '$schema': 'http://json-schema.org/draft-07/schema#',
            'id': 'generator_schema',
            'type': 'object',
            'properties': {
                'bounds': {
                    'type': 'array'
                },
                'num_qubits': {
                    'type': 'array'
                },
                'init_params': {
                    'type': ['array', 'null'],
                    'default': None
                },
                'snapshot_dir': {
                    'type': ['string', 'null'],
                    'default': None
                }
            },
            'additionalProperties': False
        }
    }

    def __init__(self,
                 bounds,
                 num_qubits,
                 generator_circuit=None,
                 init_params=None,
                 snapshot_dir=None):
        """
        Initialize the generator network.
        Args:
            bounds (numpy.ndarray): k min/max data values [[min_1,max_1],...,[min_k,max_k]],
                    given input data dim k
            num_qubits (list): k numbers of qubits to determine representation resolution,
            i.e. n qubits enable the representation of 2**n values [n_1,..., n_k]
            generator_circuit (Union): generator circuit
                UnivariateVariationalDistribution for univariate data/
                MultivariateVariationalDistribution for multivariate data, Quantum circuit
                    to implement the generator.
            init_params (Union(list, numpy.ndarray)): 1D numpy array or list, Initialization for
                                the generator's parameters.
            snapshot_dir (str): str or None, if not None save the optimizer's parameter after every
                            update step to the given directory
        Raises:
            AquaError: Set multivariate variational distribution to represent multivariate data
        """
        super().__init__()
        self._bounds = bounds
        self._num_qubits = num_qubits
        self.generator_circuit = generator_circuit
        if self.generator_circuit is None:
            entangler_map = []
            if np.sum(num_qubits) > 2:
                for i in range(int(np.sum(num_qubits))):
                    entangler_map.append(
                        [i, int(np.mod(i + 1, np.sum(num_qubits)))])
            else:
                if np.sum(num_qubits) > 1:
                    entangler_map.append([0, 1])

            if len(num_qubits) > 1:
                num_qubits = list(map(int, num_qubits))
                low = bounds[:, 0].tolist()
                high = bounds[:, 1].tolist()
                init_dist = MultivariateUniformDistribution(num_qubits,
                                                            low=low,
                                                            high=high)
                q = QuantumRegister(sum(num_qubits))
                qc = QuantumCircuit(q)
                init_dist.build(qc, q)
                init_distribution = Custom(num_qubits=sum(num_qubits),
                                           circuit=qc)
                # Set variational form
                var_form = RY(sum(num_qubits),
                              depth=1,
                              initial_state=init_distribution,
                              entangler_map=entangler_map,
                              entanglement_gate='cz')
                if init_params is None:
                    init_params = aqua_globals.random.rand(
                        var_form.num_parameters) * 2 * 1e-2
                # Set generator circuit
                self.generator_circuit = MultivariateVariationalDistribution(
                    num_qubits, var_form, init_params, low=low, high=high)
            else:
                init_dist = UniformDistribution(sum(num_qubits),
                                                low=bounds[0],
                                                high=bounds[1])
                q = QuantumRegister(sum(num_qubits), name='q')
                qc = QuantumCircuit(q)
                init_dist.build(qc, q)
                init_distribution = Custom(num_qubits=sum(num_qubits),
                                           circuit=qc)
                var_form = RY(sum(num_qubits),
                              depth=1,
                              initial_state=init_distribution,
                              entangler_map=entangler_map,
                              entanglement_gate='cz')
                if init_params is None:
                    init_params = aqua_globals.random.rand(
                        var_form.num_parameters) * 2 * 1e-2
                # Set generator circuit
                self.generator_circuit = UnivariateVariationalDistribution(
                    int(np.sum(num_qubits)),
                    var_form,
                    init_params,
                    low=bounds[0],
                    high=bounds[1])

        if len(num_qubits) > 1:
            if isinstance(self.generator_circuit,
                          MultivariateVariationalDistribution):
                pass
            else:
                raise AquaError('Set multivariate variational distribution '
                                'to represent multivariate data')
        else:
            if isinstance(self.generator_circuit,
                          UnivariateVariationalDistribution):
                pass
            else:
                raise AquaError('Set univariate variational distribution '
                                'to represent univariate data')
        # Set optimizer for updating the generator network
        self._optimizer = ADAM(maxiter=1,
                               tol=1e-6,
                               lr=1e-3,
                               beta_1=0.7,
                               beta_2=0.99,
                               noise_factor=1e-6,
                               eps=1e-6,
                               amsgrad=True,
                               snapshot_dir=snapshot_dir)

        if np.ndim(self._bounds) == 1:
            bounds = np.reshape(self._bounds, (1, len(self._bounds)))
        else:
            bounds = self._bounds
        for j, prec in enumerate(self._num_qubits):
            # prepare data grid for dim j
            grid = np.linspace(bounds[j, 0], bounds[j, 1], (2**prec))
            if j == 0:
                if len(self._num_qubits) > 1:
                    self._data_grid = [grid]
                else:
                    self._data_grid = grid
                self._grid_elements = grid
            elif j == 1:
                self._data_grid.append(grid)
                temp = []
                for g_e in self._grid_elements:
                    for g in grid:
                        temp0 = [g_e]
                        temp0.append(g)
                        temp.append(temp0)
                self._grid_elements = temp
            else:
                self._data_grid.append(grid)
                temp = []
                for g_e in self._grid_elements:
                    for g in grid:
                        temp0 = deepcopy(g_e)
                        temp0.append(g)
                        temp.append(temp0)
                self._grid_elements = deepcopy(temp)
        self._data_grid = np.array(self._data_grid)

        self._shots = None
        self._discriminator = None
        self._ret = {}

    @classmethod
    def init_params(cls, params):
        """
        Initialize via parameters dictionary and algorithm input instance.

        Args:
            params (dict): parameters dictionary

        Returns:
            QuantumGenerator: vqe object
        Raises:
            AquaError: invalid input
        """
        generator_params = params.get(Pluggable.SECTION_KEY_GENERATIVE_NETWORK)
        bounds = generator_params.get('bounds')
        if bounds is None:
            raise AquaError("Data value bounds are required.")
        num_qubits = generator_params.get('num_qubits')
        if num_qubits is None:
            raise AquaError("Numbers of qubits per dimension required.")

        init_params = generator_params.get('init_params')
        snapshot_dir = generator_params.get('snapshot_dir')

        return cls(bounds,
                   num_qubits,
                   generator_circuit=None,
                   init_params=init_params,
                   snapshot_dir=snapshot_dir)

    @classmethod
    def get_section_key_name(cls):
        return Pluggable.SECTION_KEY_GENERATIVE_NETWORK

    def set_seed(self, seed):
        """
        Set seed.
        Args:
            seed (int): seed
        """
        aqua_globals.random_seed = seed

    def set_discriminator(self, discriminator):
        """
        Set discriminator
        Args:
            discriminator (Discriminator): Discriminator used to compute the loss function.
        """
        self._discriminator = discriminator

    def construct_circuit(self, params=None):
        """
        Construct generator circuit.
        Args:
            params (numpy.ndarray): parameters which should be used to run the generator,
                    if None use self._params

        Returns:
            Instruction: construct the quantum circuit and return as gate

        """

        q = QuantumRegister(sum(self._num_qubits), name='q')
        qc = QuantumCircuit(q)
        if params is None:
            self.generator_circuit.build(qc=qc, q=q)
        else:
            generator_circuit_copy = deepcopy(self.generator_circuit)
            generator_circuit_copy.params = params
            generator_circuit_copy.build(qc=qc, q=q)

        # return qc.copy(name='qc')
        return qc.to_instruction()

    def get_output(self,
                   quantum_instance,
                   qc_state_in=None,
                   params=None,
                   shots=None):
        """
        Get data samples from the generator.
        Args:
            quantum_instance (QuantumInstance):  Quantum Instance, used to run the generator
                                        circuit.
            qc_state_in (QuantumCircuit): depreciated
            params (numpy.ndarray): array or None, parameters which should
                    be used to run the generator,
                    if None use self._params
            shots (int): if not None use a number of shots that is different from the
                        number set in quantum_instance

        Returns:
            list: generated samples, array: sample occurrence in percentage

        """
        instance_shots = quantum_instance.run_config.shots
        q = QuantumRegister(sum(self._num_qubits), name='q')
        qc = QuantumCircuit(q)
        qc.append(self.construct_circuit(params), q)
        if quantum_instance.is_statevector:
            pass
        else:
            c = ClassicalRegister(sum(self._num_qubits), name='c')
            qc.add_register(c)
            qc.measure(q, c)

        if shots is not None:
            quantum_instance.set_config(shots=shots)

        result = quantum_instance.execute(qc)

        generated_samples = []
        if quantum_instance.is_statevector:
            result = result.get_statevector(qc)
            values = np.multiply(result, np.conj(result))
            values = list(values.real)
            keys = []
            for j in range(len(values)):
                keys.append(np.binary_repr(j, int(sum(self._num_qubits))))
        else:
            result = result.get_counts(qc)
            keys = list(result)
            values = list(result.values())
            values = [float(v) / np.sum(values) for v in values]
        generated_samples_weights = values
        for i, _ in enumerate(keys):
            index = 0
            temp = []
            for k, p in enumerate(self._num_qubits):
                bin_rep = 0
                j = 0
                while j < p:
                    bin_rep += int(keys[i][index]) * 2**(int(p) - j - 1)
                    j += 1
                    index += 1
                if len(self._num_qubits) > 1:
                    temp.append(self._data_grid[k][int(bin_rep)])
                else:
                    temp.append(self._data_grid[int(bin_rep)])
            generated_samples.append(temp)

        self.generator_circuit._probabilities = generated_samples_weights
        if shots is not None:
            # Restore the initial quantum_instance configuration
            quantum_instance.set_config(shots=instance_shots)
        return generated_samples, generated_samples_weights

    def loss(self, x, weights):  # pylint: disable=arguments-differ
        """
        Loss function
        Args:
            x (numpy.ndarray): sample label (equivalent to discriminator output)
            weights (numpy.ndarray): probability for measuring the sample

        Returns:
            float: loss function

        """
        try:
            # pylint: disable=no-member
            loss = (-1) * np.dot(np.log(x).transpose(), weights)
        except Exception:  # pylint: disable=broad-except
            loss = (-1) * np.dot(np.log(x), weights)
        return loss.flatten()

    def _get_objective_function(self, quantum_instance, discriminator):
        """
        Get objective function
        Args:
            quantum_instance (QuantumInstance): used to run the quantum circuit.
            discriminator (torch.nn.Module): discriminator network to compute the sample labels.

        Returns:
            objective_function: objective function for quantum generator optimization

        """
        def objective_function(params):
            """
            Objective function
            Args:
                params (numpy.ndarray): generator parameters

            Returns:
                self.loss: loss function

            """
            generated_data, generated_prob = self.get_output(quantum_instance,
                                                             params=params,
                                                             shots=self._shots)
            prediction_generated = discriminator.get_label(generated_data,
                                                           detach=True)
            return self.loss(prediction_generated, generated_prob)

        return objective_function

    def train(self, quantum_instance=None, shots=None):
        """
        Perform one training step w.r.t to the generator's parameters
        Args:
            quantum_instance (QuantumInstance): used to run the generator circuit.
            shots (int): Number of shots for hardware or qasm execution.

        Returns:
            dict: generator loss(float) and updated parameters (array).
        """

        self._shots = shots
        # Force single optimization iteration
        self._optimizer._maxiter = 1
        self._optimizer._t = 0
        objective = self._get_objective_function(quantum_instance,
                                                 self._discriminator)
        self.generator_circuit.params, loss, _ = \
            self._optimizer.optimize(num_vars=len(self.generator_circuit.params),
                                     objective_function=objective,
                                     initial_point=self.generator_circuit.params)

        self._ret['loss'] = loss
        self._ret['params'] = self.generator_circuit.params

        return self._ret
Beispiel #8
0
    def test_ecev(self, use_circuits):
        """ European Call Expected Value test """
        if not use_circuits:
            # ignore deprecation warnings from the deprecation of VariationalForm as input for
            # the univariate variational distribution
            warnings.filterwarnings("ignore", category=DeprecationWarning)

        bounds = np.array([0., 7.])
        num_qubits = [3]
        entangler_map = []
        for i in range(sum(num_qubits)):
            entangler_map.append([i, int(np.mod(i + 1, sum(num_qubits)))])

        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(int(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)
        var_form = RY(int(np.sum(num_qubits)),
                      depth=1,
                      initial_state=init_distribution,
                      entangler_map=entangler_map,
                      entanglement_gate='cz')
        if use_circuits:
            theta = ParameterVector('θ', var_form.num_parameters)
            var_form = var_form.construct_circuit(theta)

        uncertainty_model = UnivariateVariationalDistribution(int(
            sum(num_qubits)),
                                                              var_form,
                                                              g_params,
                                                              low=bounds[0],
                                                              high=bounds[1])

        if use_circuits:
            uncertainty_model._var_form_params = theta

        strike_price = 2
        c_approx = 0.25
        european_call = EuropeanCallExpectedValue(uncertainty_model,
                                                  strike_price=strike_price,
                                                  c_approx=c_approx)

        uncertainty_model.set_probabilities(
            QuantumInstance(BasicAer.get_backend('statevector_simulator')))

        algo = AmplitudeEstimation(5, european_call)
        result = algo.run(
            quantum_instance=BasicAer.get_backend('statevector_simulator'))
        self.assertAlmostEqual(result['estimation'], 1.2580, places=4)
        self.assertAlmostEqual(result['max_probability'], 0.8785, places=4)

        if not use_circuits:
            warnings.filterwarnings(action="always",
                                    category=DeprecationWarning)
Beispiel #9
0
def QGAN_method(kk, num_qubit, epochs, batch, bound, snap, data):
    start = time.time()

    real_data = data

    # In[41]:

    # Number training data samples
    N = 1000

    # Load data samples from log-normal distribution with mean=1 and standard deviation=1
    mu = 1
    sigma = 1

    # real_data = np.random.lognormal(mean = mu, sigma=sigma, size=N)
    # print(real_data)

    # Set the data resolution
    # Set upper and lower data values as list of k min/max data values [[min_0,max_0],...,[min_k-1,max_k-1]]
    bounds = np.array([0, bound])

    # Set number of qubits per data dimension as list of k qubit values[#q_0,...,#q_k-1]
    num_qubits = [num_qubit]

    k = kk

    # In[52]:

    # Set number of training epochs
    # Note: The algorithm's runtime can be shortened by reducing the number of training epochs.
    num_epochs = epochs
    # Batch size
    batch_size = batch

    # Initialize qGAN
    qgan = QGAN(real_data,
                bounds,
                num_qubits,
                batch_size,
                num_epochs,
                snapshot_dir=snap)
    qgan.seed = 1
    # Set quantum instance to run the quantum generator
    quantum_instance = QuantumInstance(
        backend=BasicAer.get_backend('statevector_simulator'))

    # Set entangler map
    entangler_map = [[0, 1]]

    # Set an initial state for the generator circuit
    init_dist = UniformDistribution(sum(num_qubits),
                                    low=bounds[0],
                                    high=bounds[1])
    q = QuantumRegister(sum(num_qubits), name='q')
    qc = QuantumCircuit(q)
    init_dist.build(qc, q)
    init_distribution = Custom(num_qubits=sum(num_qubits), circuit=qc)
    var_form = RY(int(np.sum(num_qubits)),
                  depth=k,
                  initial_state=init_distribution,
                  entangler_map=entangler_map,
                  entanglement_gate='cz')

    # Set generator's initial parameters
    init_params = aqua_globals.random.rand(
        var_form._num_parameters) * 2 * np.pi
    # Set generator circuit
    g_circuit = UnivariateVariationalDistribution(int(sum(num_qubits)),
                                                  var_form,
                                                  init_params,
                                                  low=bounds[0],
                                                  high=bounds[1])
    # Set quantum generator
    qgan.set_generator(generator_circuit=g_circuit)
    # Set classical discriminator neural network
    discriminator = NumPyDiscriminator(len(num_qubits))
    qgan.set_discriminator(discriminator)

    # In[53]:

    # Run qGAN
    qgan.run(quantum_instance)

    # Runtime
    end = time.time()
    print('qGAN training runtime: ', (end - start) / 60., ' min')

    return qgan
Beispiel #10
0
# 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
c_approx = 0.25

# construct circuit factory for payoff function
european_call = EuropeanCallExpectedValue(
    uncertainty_model,