def get_bars_and_stripes_target_distribution(nrows,
                                             ncols,
                                             fraction=1.,
                                             method="zigzag"):
    ''' Generates bars and stripes (BAS) data in zigzag pattern
    Args: 
        nrows (int): number of rows in BAS dataset 
        ncols (int): number of columns in BAS dataset 
        fraction (float): maximum fraction of patterns to include (at least one pattern will always be included)
        method (string): the method to use to label the qubits
    Returns: 
        Array of list of BAS pattern. 
    '''
    if method == "zigzag":
        data = bars_and_stripes_zigzag(nrows, ncols)
    else:
        raise RuntimeError(
            "Method: {} is not supported for generated a target distribution for bars and stripes"
            .format(method))

    # Remove patterns until left with a subset that has cardinality less than or equal to the percentage * total number of patterns
    num_desired_patterns = int(len(data) * fraction)
    num_desired_patterns = max(num_desired_patterns, 1)
    data = random.sample(list(data), num_desired_patterns)

    distribution_dict = {}
    for pattern in data:
        bitstring = ""
        for qubit in pattern:
            bitstring += str(qubit)

        distribution_dict[bitstring] = 1.

    return BitstringDistribution(distribution_dict)
def get_thermal_sampled_distribution(
    n_samples: int,
    n_spins: int,
    temperature: float,
    hamiltonian_parameters: typing.Tuple[np.ndarray, np.ndarray],
) -> BitstringDistribution:
    """Generates thermal states sample distribution
    Args:
        n_samples: the number of samples from the original distribution
        n_spins: number of spins in the Ising system
        temperature: temperature factor in the Boltzmann distribution
    Returns:
       histogram_samples: keys are binary string representations and values are corresponding probabilities.
    """
    distribution = get_thermal_target_bitstring_distribution(
        n_spins, temperature, hamiltonian_parameters
    ).distribution_dict
    sample_distribution_dict = sample_from_probability_distribution(
        distribution, n_samples
    )
    histogram_samples = np.zeros(2 ** n_spins)
    for samples, counts in sample_distribution_dict.items():
        integer_list: typing.List[int] = []
        for elem in samples:
            integer_list.append(int(elem))
        idx = convert_ising_bitstring_to_integer(integer_list)
        histogram_samples[idx] += counts / n_samples

    for spin in range(int(2 ** n_spins)):
        binary_bitstring = convert_tuples_to_bitstrings([dec2bin(spin, n_spins)])[0]
        sample_distribution_dict[binary_bitstring] = histogram_samples[spin]

    return BitstringDistribution(sample_distribution_dict)
    def test_get_measurements_representing_distribution_randomly_samples_leftover_bitstrings_when_probabilities_equal(
        self, ):
        random.seed(RNDSEED)
        bitstring_distribution = BitstringDistribution({"00": 0.5, "11": 0.5})
        number_of_samples = 51
        max_number_of_trials = 10
        got_different_measurements = False
        previous_measurements = Measurements.get_measurements_representing_distribution(
            bitstring_distribution, number_of_samples)

        while not got_different_measurements:
            measurements = Measurements.get_measurements_representing_distribution(
                bitstring_distribution, number_of_samples)

            assert measurements.get_counts() == {
                "00": 25,
                "11": 26,
            } or measurements.get_counts() == {
                "00": 26,
                "11": 25
            }

            if measurements.get_counts() != previous_measurements.get_counts():
                got_different_measurements = True

            max_number_of_trials -= 1
            if max_number_of_trials == 0:
                break
        assert got_different_measurements
    def setUp(self):
        number_of_layers = 1
        number_of_qubits = 4
        topology = "all"
        self.ansatz = QCBMAnsatz(number_of_layers, number_of_qubits, topology)
        self.target_bitstring_distribution = BitstringDistribution({
            "0000": 1.0,
            "0001": 0.0,
            "0010": 0.0,
            "0011": 1.0,
            "0100": 0.0,
            "0101": 1.0,
            "0110": 0.0,
            "0111": 0.0,
            "1000": 0.0,
            "1001": 0.0,
            "1010": 1.0,
            "1011": 0.0,
            "1100": 1.0,
            "1101": 0.0,
            "1110": 0.0,
            "1111": 1.0,
        })

        self.backend = create_object({
            "module_name": "zquantum.core.interfaces.mock_objects",
            "function_name": "MockQuantumSimulator",
            "n_samples": 1,
        })

        self.gradient_types = ["finite_difference"]
def QCBMCostFunction(
    ansatz: Ansatz,
    backend: QuantumBackend,
    distance_measure: Callable,
    distance_measure_parameters: dict,
    target_bitstring_distribution: BitstringDistribution,
    gradient_type: str = "finite_difference",
):
    """Cost function used for evaluating QCBM.

    Args:
        ansatz (zquantum.core.interfaces.ansatz.Ansatz): the ansatz used to construct the variational circuits
        backend (zquantum.core.interfaces.backend.QuantumBackend): backend used for QCBM evaluation
        distance_measure (callable): function used to calculate the distance measure
        distance_measure_parameters (dict): dictionary containing the relevant parameters for the chosen distance measure
        target_bitstring_distribution (zquantum.core.bitstring_distribution.BitstringDistribution): bistring distribution which QCBM aims to learn
        gradient_type (str): parameter indicating which type of gradient should be used.

    Returns:
        Callable that evaluates the parametrized circuit produced by the ansatz with the given parameters and returns
            the distance between the produced bitstring distribution and the target distribution
    """

    assert (int(target_bitstring_distribution.get_qubits_number()) ==
            ansatz.number_of_qubits)

    def cost_function(parameters: np.ndarray,
                      store_artifact: StoreArtifact = None) -> ValueEstimate:
        """
        Evaluates the value of the cost function for given parameters.

        Args:
            parameters: parameters for which the evaluation should occur.

        Returns:
            (float): cost function value for given parameters
            zquantum.core.bitstring_distribution.BitstringDistribution: distribution obtained
        """
        circuit = ansatz.get_executable_circuit(parameters)
        distribution = backend.get_bitstring_distribution(circuit)
        value = evaluate_distribution_distance(
            target_bitstring_distribution,
            distribution,
            distance_measure,
            distance_measure_parameters=distance_measure_parameters,
        )

        if store_artifact:
            store_artifact("bitstring_distribution", distribution)

        return ValueEstimate(value)

    if gradient_type == "finite_difference":
        cost_function = function_with_gradient(
            cost_function, finite_differences_gradient(cost_function))
    else:
        raise RuntimeError("Unsupported gradient type: ", gradient_type)

    return cost_function
def get_thermal_target_bitstring_distribution(
    n_spins: int,
    temperature: float,
    hamiltonian_parameters: typing.Tuple[np.ndarray, np.ndarray],
) -> BitstringDistribution:
    """Generates thermal states target distribution, saved in a dict where keys are bitstrings and
    values are corresponding probabilities according to the Boltzmann Distribution formula.

    Args:
        n_spins: positive number of spins in the Ising system
        temperature: temperature factor in the boltzman distribution
        hamiltonian_parameters: values of hamiltonian parameters, namely external fields and two body couplings.

    Returns:
       Thermal target distribution.
       Number of positive spins in the spin state.
    """
    partition_function = 0
    external_fields, two_body_couplings = hamiltonian_parameters
    beta = 1.0 / temperature
    distribution = {}

    for spin in range(int(2 ** n_spins)):
        ising_bitstring = convert_integer_to_ising_bitstring(spin, n_spins)
        energy = 0
        for i in range(n_spins):
            energy -= ising_bitstring[i] * external_fields[i]
            if i != n_spins - 1:
                energy -= (
                    ising_bitstring[i]
                    * ising_bitstring[i + 1]
                    * two_body_couplings[i, i + 1]
                )
        boltzmann_factor = np.exp(energy * beta)
        partition_function += boltzmann_factor

        binary_bitstring = convert_tuples_to_bitstrings([dec2bin(spin, n_spins)])[0]
        distribution[binary_bitstring] = boltzmann_factor

    normalized_distribution = {
        key: value / partition_function for key, value in distribution.items()
    }

    return BitstringDistribution(normalized_distribution)
Exemple #7
0
def _create_QCBM_cost_function(
    ansatz: Ansatz,
    backend: QuantumBackend,
    n_samples: int,
    distance_measure: DistanceMeasure,
    distance_measure_parameters: dict,
    target_bitstring_distribution: BitstringDistribution,
):
    assert (int(target_bitstring_distribution.get_qubits_number()) ==
            ansatz.number_of_qubits)

    def cost_function(parameters: np.ndarray,
                      store_artifact: StoreArtifact = None) -> ValueEstimate:
        """
        Evaluates the value of the cost function for given parameters.

        Args:
            parameters: parameters for which the evaluation should occur.
            store_artifact: callable defining how the bitstring distributions should be stored.
        """
        # TODO: we use private method here due to performance reasons.
        # This should be fixed once better mechanism for handling it will be implemented.
        # In case of questions ask mstechly.
        # circuit = ansatz.get_executable_circuit(parameters)
        circuit = ansatz._generate_circuit(parameters)
        distribution = backend.get_bitstring_distribution(circuit, n_samples)
        value = evaluate_distribution_distance(
            target_bitstring_distribution,
            distribution,
            distance_measure,
            distance_measure_parameters=distance_measure_parameters,
        )

        if store_artifact:
            store_artifact("bitstring_distribution", distribution)

        return ValueEstimate(value)

    return cost_function
        HistoryEntry(
            call_number=0,
            params=np.array([0.1, 0.2, 0.3j]),
            value=ValueEstimate(0.5, precision=6),
        ),
        HistoryEntry(call_number=1, params=np.array([1, 2, 3]), value=-10.0),
        HistoryEntryWithArtifacts(
            call_number=2,
            params=np.array([-1, -0.5, -0.6]),
            value=-20.0,
            artifacts={
                "bitstring":
                "0111",
                "bitstring_distribution":
                BitstringDistribution({
                    "111": 0.25,
                    "010": 0.75
                }),
            },
        ),
    ],
)

EXPECTED_DESERIALIZED_RESULT = {
    "schema":
    "zapata-v1-optimization_result",
    "opt_value":
    0.5,
    "opt_params":
    convert_array_to_dict(np.array([0, 0.5, 2.5])),
    "nit":
    3,
class TestMeasurements:
    def test_io(self):
        # Given
        measurements_data = {
            "schema":
            SCHEMA_VERSION + "-measurements",
            "counts": {
                "000": 1,
                "001": 2,
                "010": 1,
                "011": 1,
                "100": 1,
                "101": 1,
                "110": 1,
                "111": 1,
            },
            "bitstrings": [
                [0, 0, 0],
                [0, 0, 1],
                [0, 1, 0],
                [0, 1, 1],
                [1, 0, 0],
                [1, 1, 0],
                [1, 1, 1],
                [1, 0, 1],
                [0, 0, 1],
            ],
        }
        input_filename = "measurements_input_test.json"
        output_filename = "measurements_output_test.json"

        with open(input_filename, "w") as f:
            f.write(json.dumps(measurements_data, indent=2))

        # When
        measurements = Measurements.load_from_file(input_filename)
        measurements.save(output_filename)

        # Then
        with open(output_filename, "r") as f:
            output_data = json.load(f)
        assert measurements_data == output_data

        remove_file_if_exists(input_filename)
        remove_file_if_exists(output_filename)

    def test_save_for_numpy_integers(self):
        # Given
        target_bitstrings = [(0, 0, 0)]
        input_bitstrings = [(np.int8(0), np.int8(0), np.int8(0))]

        filename = "measurementstest.json"
        measurements = Measurements(input_bitstrings)
        target_measurements = Measurements(target_bitstrings)

        # When
        measurements.save(filename)

        # Then
        recreated_measurements = Measurements.load_from_file(filename)
        assert target_measurements.bitstrings == recreated_measurements.bitstrings
        remove_file_if_exists("measurementstest.json")

    def test_intialize_with_bitstrings(self):
        # Given
        bitstrings = [
            (0, 0, 0),
            (0, 0, 1),
            (0, 0, 1),
            (0, 1, 0),
            (0, 1, 1),
            (1, 0, 0),
            (1, 0, 1),
            (1, 1, 0),
            (1, 1, 1),
        ]

        # When
        measurements = Measurements(bitstrings=bitstrings)

        # Then
        assert measurements.bitstrings == [
            (0, 0, 0),
            (0, 0, 1),
            (0, 0, 1),
            (0, 1, 0),
            (0, 1, 1),
            (1, 0, 0),
            (1, 0, 1),
            (1, 1, 0),
            (1, 1, 1),
        ]

    def test_intialize_with_counts(self):
        # Given
        counts = {
            "000": 1,
            "001": 2,
            "010": 1,
            "011": 1,
            "100": 1,
            "101": 1,
            "110": 1,
            "111": 1,
        }

        # When
        measurements = Measurements.from_counts(counts)

        # Then
        assert measurements.bitstrings == [
            (0, 0, 0),
            (0, 0, 1),
            (0, 0, 1),
            (0, 1, 0),
            (0, 1, 1),
            (1, 0, 0),
            (1, 0, 1),
            (1, 1, 0),
            (1, 1, 1),
        ]

    def test_bitstrings(self):
        # Given
        measurements_data = {
            "schema":
            SCHEMA_VERSION + "-measurements",
            "counts": {
                "000": 1,
                "001": 2,
                "010": 1,
                "011": 1,
                "100": 1,
                "101": 1,
                "110": 1,
                "111": 1,
            },
            "bitstrings": [
                [0, 0, 0],
                [0, 0, 1],
                [0, 1, 0],
                [0, 1, 1],
                [1, 0, 0],
                [1, 1, 0],
                [1, 1, 1],
                [1, 0, 1],
                [0, 0, 1],
            ],
        }
        input_filename = "measurements_input_test.json"

        with open(input_filename, "w") as f:
            f.write(json.dumps(measurements_data, indent=2))
        measurements = Measurements.load_from_file(input_filename)

        # When/Then
        assert measurements.bitstrings == [
            (0, 0, 0),
            (0, 0, 1),
            (0, 1, 0),
            (0, 1, 1),
            (1, 0, 0),
            (1, 1, 0),
            (1, 1, 1),
            (1, 0, 1),
            (0, 0, 1),
        ]

        remove_file_if_exists(input_filename)

    def test_get_counts(self):
        # Given
        measurements_data = {
            "schema":
            SCHEMA_VERSION + "-measurements",
            "counts": {
                "000": 1,
                "001": 2,
                "010": 1,
                "011": 1,
                "100": 1,
                "101": 1,
                "110": 1,
                "111": 1,
            },
            "bitstrings": [
                [0, 0, 0],
                [0, 0, 1],
                [0, 1, 0],
                [0, 1, 1],
                [1, 0, 0],
                [1, 1, 0],
                [1, 1, 1],
                [1, 0, 1],
                [0, 0, 1],
            ],
        }
        input_filename = "measurements_input_test.json"

        with open(input_filename, "w") as f:
            f.write(json.dumps(measurements_data, indent=2))
        measurements = Measurements.load_from_file(input_filename)

        # When
        counts = measurements.get_counts()

        # Then
        assert measurements_data["counts"] == counts

        remove_file_if_exists(input_filename)

    def test_get_distribution(self):
        # Given
        measurements_data = {
            "schema":
            SCHEMA_VERSION + "-measurements",
            "counts": {
                "000": 1,
                "001": 2,
                "010": 1,
                "011": 1,
                "100": 1,
                "101": 1,
                "110": 1,
                "111": 1,
            },
            "bitstrings": [
                [0, 0, 0],
                [0, 0, 1],
                [0, 1, 0],
                [0, 1, 1],
                [1, 0, 0],
                [1, 1, 0],
                [1, 1, 1],
                [1, 0, 1],
                [0, 0, 1],
            ],
        }
        input_filename = "measurements_input_test.json"

        with open(input_filename, "w") as f:
            f.write(json.dumps(measurements_data, indent=2))
        measurements = Measurements.load_from_file(input_filename)

        # When
        distribution = measurements.get_distribution()

        # Then
        assert distribution.distribution_dict == {
            "000": 1 / 9,
            "001": 2 / 9,
            "010": 1 / 9,
            "011": 1 / 9,
            "100": 1 / 9,
            "101": 1 / 9,
            "110": 1 / 9,
            "111": 1 / 9,
        }

        remove_file_if_exists(input_filename)

    def test_add_counts(self):
        # Given
        measurements = Measurements()
        measurements_counts = {
            "000": 1,
            "001": 2,
            "010": 1,
            "011": 1,
            "100": 1,
            "101": 1,
            "110": 1,
            "111": 1,
        }

        # When
        measurements.add_counts(measurements_counts)

        # Then
        assert measurements.bitstrings == [
            (0, 0, 0),
            (0, 0, 1),
            (0, 0, 1),
            (0, 1, 0),
            (0, 1, 1),
            (1, 0, 0),
            (1, 0, 1),
            (1, 1, 0),
            (1, 1, 1),
        ]
        assert measurements.get_counts() == {
            "000": 1,
            "001": 2,
            "010": 1,
            "011": 1,
            "100": 1,
            "101": 1,
            "110": 1,
            "111": 1,
        }

    def test_add_measurements(self):
        # Given
        measurements = Measurements()
        bitstrings = [
            (0, 0, 0),
            (0, 0, 1),
            (0, 0, 1),
            (0, 1, 0),
            (0, 1, 1),
            (1, 0, 0),
            (1, 0, 1),
            (1, 1, 0),
            (1, 1, 1),
        ]

        # When
        measurements.bitstrings = bitstrings

        # Then
        assert measurements.bitstrings == [
            (0, 0, 0),
            (0, 0, 1),
            (0, 0, 1),
            (0, 1, 0),
            (0, 1, 1),
            (1, 0, 0),
            (1, 0, 1),
            (1, 1, 0),
            (1, 1, 1),
        ]

        # When
        measurements.bitstrings += bitstrings

        # Then
        assert measurements.bitstrings == [
            (0, 0, 0),
            (0, 0, 1),
            (0, 0, 1),
            (0, 1, 0),
            (0, 1, 1),
            (1, 0, 0),
            (1, 0, 1),
            (1, 1, 0),
            (1, 1, 1),
            (0, 0, 0),
            (0, 0, 1),
            (0, 0, 1),
            (0, 1, 0),
            (0, 1, 1),
            (1, 0, 0),
            (1, 0, 1),
            (1, 1, 0),
            (1, 1, 1),
        ]

    def test_get_expectation_values_from_measurements(self):
        # Given
        measurements = Measurements([(0, 1, 0), (0, 1, 0), (0, 0, 0),
                                     (1, 0, 0), (1, 1, 1)])
        ising_operator = IsingOperator("10[] + [Z0 Z1] - 15[Z1 Z2]")
        target_expectation_values = np.array([10, -0.2, -3])
        target_correlations = np.array([[100, -2, -30], [-2, 1, -9],
                                        [-30, -9, 225]])
        denominator = len(measurements.bitstrings)
        covariance_11 = (target_correlations[1, 1] -
                         target_expectation_values[1]**2) / denominator
        covariance_12 = (target_correlations[1, 2] -
                         target_expectation_values[1] *
                         target_expectation_values[2]) / denominator
        covariance_22 = (target_correlations[2, 2] -
                         target_expectation_values[2]**2) / denominator

        target_covariances = np.array([
            [0, 0, 0],
            [0, covariance_11, covariance_12],
            [0, covariance_12, covariance_22],
        ])

        # When
        expectation_values = measurements.get_expectation_values(
            ising_operator, False)
        # Then
        np.testing.assert_allclose(expectation_values.values,
                                   target_expectation_values)
        assert len(expectation_values.correlations) == 1
        np.testing.assert_allclose(expectation_values.correlations[0],
                                   target_correlations)
        assert len(expectation_values.estimator_covariances) == 1
        np.testing.assert_allclose(expectation_values.estimator_covariances[0],
                                   target_covariances)

    def test_get_expectation_values_from_measurements_with_bessel_correction(
            self):
        # Given
        measurements = Measurements([(0, 1, 0), (0, 1, 0), (0, 0, 0),
                                     (1, 0, 0), (1, 1, 1)])
        ising_operator = IsingOperator("10[] + [Z0 Z1] - 15[Z1 Z2]")
        target_expectation_values = np.array([10, -0.2, -3])
        target_correlations = np.array([[100, -2, -30], [-2, 1, -9],
                                        [-30, -9, 225]])
        denominator = len(measurements.bitstrings) - 1
        covariance_11 = (target_correlations[1, 1] -
                         target_expectation_values[1]**2) / denominator
        covariance_12 = (target_correlations[1, 2] -
                         target_expectation_values[1] *
                         target_expectation_values[2]) / denominator
        covariance_22 = (target_correlations[2, 2] -
                         target_expectation_values[2]**2) / denominator

        target_covariances = np.array([
            [0, 0, 0],
            [0, covariance_11, covariance_12],
            [0, covariance_12, covariance_22],
        ])

        # When
        expectation_values = measurements.get_expectation_values(
            ising_operator, True)
        # Then
        np.testing.assert_allclose(expectation_values.values,
                                   target_expectation_values)
        assert len(expectation_values.correlations) == 1
        np.testing.assert_allclose(expectation_values.correlations[0],
                                   target_correlations)
        assert len(expectation_values.estimator_covariances) == 1
        np.testing.assert_allclose(expectation_values.estimator_covariances[0],
                                   target_covariances)

    @pytest.mark.parametrize(
        "bitstring_distribution, number_of_samples",
        [
            (BitstringDistribution({
                "00": 0.5,
                "11": 0.5
            }), 1),
            (BitstringDistribution({
                "00": 0.5,
                "11": 0.5
            }), 10),
            (BitstringDistribution({
                "00": 0.5,
                "11": 0.5
            }), 51),
            (BitstringDistribution({
                "00": 0.5,
                "11": 0.5
            }), 137),
            (BitstringDistribution({
                "00": 0.5,
                "11": 0.5
            }), 5000),
            (BitstringDistribution({
                "0000": 0.137,
                "0001": 0.863
            }), 100),
            (
                BitstringDistribution({
                    "00": 0.1234,
                    "01": 0.5467,
                    "10": 0.0023,
                    "11": 0.3276
                }),
                100,
            ),
            (
                BitstringDistribution({
                    "0000": 0.06835580857498666,
                    "1000": 0.060975627112613416,
                    "0100": 0.05976605586194627,
                    "1100": 0.07138587439957303,
                    "0010": 0.06474168297455969,
                    "1010": 0.0825036470378936,
                    "0110": 0.09861252446183953,
                    "1110": 0.0503013698630137,
                    "0001": 0.04496424123821384,
                    "1001": 0.07317221135029355,
                    "0101": 0.08171161714997331,
                    "1101": 0.03753940579967977,
                    "0011": 0.05157676570005337,
                    "1011": 0.05,
                    "0111": 0.04964419142501335,
                    "1111": 0.05474897705034692,
                }),
                5621,
            ),
        ],
    )
    def test_get_measurements_representing_distribution_returns_right_number_of_samples(
            self, bitstring_distribution, number_of_samples):
        measurements = Measurements.get_measurements_representing_distribution(
            bitstring_distribution, number_of_samples)
        assert len(measurements.bitstrings) == number_of_samples

    @pytest.mark.parametrize(
        "bitstring_distribution, number_of_samples, expected_counts",
        [
            (
                BitstringDistribution({
                    "01": 0.3333333,
                    "11": (1 - 0.3333333)
                }),
                3,
                {
                    "01": 1,
                    "11": 2
                },
            ),
            (
                BitstringDistribution({
                    "01": 0.9999999,
                    "11": (1 - 0.9999999)
                }),
                1,
                {
                    "01": 1
                },
            ),
        ],
    )
    def test_get_measurements_representing_distribution_correctly_samples_leftover_bitstrings(
            self, bitstring_distribution, number_of_samples, expected_counts):
        random.seed(RNDSEED)
        measurements = Measurements.get_measurements_representing_distribution(
            bitstring_distribution, number_of_samples)
        assert measurements.get_counts() == expected_counts

    def test_get_measurements_representing_distribution_randomly_samples_leftover_bitstrings_when_probabilities_equal(
        self, ):
        random.seed(RNDSEED)
        bitstring_distribution = BitstringDistribution({"00": 0.5, "11": 0.5})
        number_of_samples = 51
        max_number_of_trials = 10
        got_different_measurements = False
        previous_measurements = Measurements.get_measurements_representing_distribution(
            bitstring_distribution, number_of_samples)

        while not got_different_measurements:
            measurements = Measurements.get_measurements_representing_distribution(
                bitstring_distribution, number_of_samples)

            assert measurements.get_counts() == {
                "00": 25,
                "11": 26,
            } or measurements.get_counts() == {
                "00": 26,
                "11": 25
            }

            if measurements.get_counts() != previous_measurements.get_counts():
                got_different_measurements = True

            max_number_of_trials -= 1
            if max_number_of_trials == 0:
                break
        assert got_different_measurements

    @pytest.mark.parametrize(
        "bitstring_distribution",
        [
            BitstringDistribution({
                "00": 0.5,
                "11": 0.5
            }),
            BitstringDistribution({
                "000": 0.5,
                "101": 0.5
            }),
            BitstringDistribution({
                "0000": 0.137,
                "0001": 0.863
            }),
            BitstringDistribution({
                "00": 0.1234,
                "01": 0.5467,
                "10": 0.0023,
                "11": 0.3276
            }),
        ],
    )
    def test_get_measurements_representing_distribution_gives_exactly_right_counts(
            self, bitstring_distribution):
        number_of_samples = 10000
        measurements = Measurements.get_measurements_representing_distribution(
            bitstring_distribution, number_of_samples)

        counts = measurements.get_counts()
        for bitstring, probability in bitstring_distribution.distribution_dict.items(
        ):
            assert probability * number_of_samples == counts[bitstring]
from zquantum.qcbm.cost_function import QCBMCostFunction, create_QCBM_cost_function

number_of_layers = 1
number_of_qubits = 4
topology = "all"
ansatz = QCBMAnsatz(number_of_layers, number_of_qubits, topology)
target_bitstring_distribution = BitstringDistribution(
    {
        "0000": 1.0,
        "0001": 0.0,
        "0010": 0.0,
        "0011": 1.0,
        "0100": 0.0,
        "0101": 1.0,
        "0110": 0.0,
        "0111": 0.0,
        "1000": 0.0,
        "1001": 0.0,
        "1010": 1.0,
        "1011": 0.0,
        "1100": 1.0,
        "1101": 0.0,
        "1110": 0.0,
        "1111": 1.0,
    }
)

backend = create_object(
    {
        "module_name": "zquantum.core.symbolic_simulator",
        "function_name": "SymbolicSimulator",
    }