Example #1
0
    def solve_qa(self, verbose=True, num_reads=100):
        assert self.token is not None
        assert self.mode == "bqm"
        if self.qubo is None:
            self.pre_process()
        if verbose:
            print(f"Solving SMTI with: {SOLVER}")
            print(f"Optimal Solution: {-(len(self.encoding) * self.p2 + self.matching.size * self.p1)}")

        chain_strength = self.get_chain_stength() + 1  # max element in qubo matrix + epsilon
        solver_limit = len(self.encoding)  # solver_limit => size of qubo matrix

        G = nx.complete_graph(solver_limit)
        dw_solver = DWaveSampler(solver=SOLVER, token=self.token, endpoint=ENDPOINT)
        embedding = minorminer.find_embedding(G.edges, dw_solver.edgelist)
        fixed_embedding = FixedEmbeddingComposite(dw_solver, embedding)
        result = fixed_embedding.sample(self.qubo, num_reads=num_reads, chain_strength=chain_strength)

        dw_solver.client.close()  # clean up all the thread mess the client creates so it does not block my code
        if verbose:
            print(result)
            for index, (sample, energy, occ, chain) in enumerate(result.record):
                match_, _ = self.encode_qa(sample.tolist())
                stable_, size_ = Solution(self.matching, match_).is_stable()
                print(f"{index}: ", match_, size_, stable_)

        samples = pd.DataFrame()
        for sample, energy, occ, chain in result.record:
            match, valid = self.encode_qa(sample.tolist())
            stable, size = Solution(self.matching, match).is_stable()
            samples = samples.append({"match": match, "sample": sample.tolist(),
                                      "energy": energy, "occ": occ, "chain": chain,
                                      "valid": valid, "stable": stable, "size": size}, ignore_index=True)
        return samples
Example #2
0
    def test_problem_label_in_sampleset(self):
        """All data adapters should propagate problem label."""

        # sample bqm -> sampleset
        qpu = DWaveSampler()
        sampler = FixedEmbeddingComposite(qpu, self.embedding)
        sampleset = sampler.sample(self.bqm, label=self.label, **self.params)

        # ensure `from_bqm_sampleset` adapter propagates label
        data = from_bqm_sampleset(self.bqm,
                                  sampleset,
                                  sampler,
                                  params=self.params)
        self.assertEqual(data['details']['label'], self.label)
Example #3
0
    def test_sampler_graph_validation(self):
        """All data adapters should fail on non-Chimera/Pegasus solvers."""

        # sample
        qpu = DWaveSampler()
        sampler = FixedEmbeddingComposite(qpu, self.embedding)
        sampleset = sampler.sample(self.bqm,
                                   return_embedding=True,
                                   **self.params)

        # resolve it before we mangle with it
        sampleset.info['problem_id']
        # change solver topology to non-chimera/pegasus to test solver validation
        sampler.child.solver.properties['topology']['type'] = 'unknown'

        # ensure `from_bqm_sampleset` adapter fails on unstructured solver
        with self.assertRaises(TypeError):
            from_bqm_sampleset(self.bqm,
                               sampleset,
                               sampler,
                               params=self.params)
Example #4
0
    def test_sampler_type_validation(self):
        """All data adapters should fail on non-StructuredSolvers."""

        # sample
        qpu = DWaveSampler()
        sampler = FixedEmbeddingComposite(qpu, self.embedding)
        sampleset = sampler.sample(self.bqm,
                                   return_embedding=True,
                                   **self.params)

        # resolve it before we mangle with it
        sampleset.info['problem_id']
        # change solver to unstructured to test solver validation
        sampler.child.solver = unstructured_solver_mock

        # ensure `from_bqm_sampleset` adapter fails on unstructured solver
        with self.assertRaises(TypeError):
            from_bqm_sampleset(self.bqm,
                               sampleset,
                               sampler,
                               params=self.params)
Example #5
0
    def _test_from_bqm_sampleset(self, bqm):
        # sample
        qpu = DWaveSampler()
        sampler = FixedEmbeddingComposite(qpu, self.embedding)
        sampleset = sampler.sample(bqm,
                                   return_embedding=True,
                                   chain_strength=self.chain_strength,
                                   **self.params)

        # convert
        data = from_bqm_sampleset(bqm, sampleset, sampler, params=self.params)

        # construct (unembedded) response with chain breaks resolved
        # NOTE: for bqm/sampleset adapter, this is the best we can expect :(

        # inverse the embedding
        var_to_idx = {var: idx for idx, var in enumerate(sampleset.variables)}
        unembedding = {
            q: var_to_idx[v]
            for v, qs in self.embedding.items() for q in qs
        }

        # embed sampleset
        solutions_without_chain_breaks = [[
            int(sample[unembedding[q]]) if q in unembedding else val
            for q, val in enumerate(solution)
        ] for solution, sample in zip(self.response['solutions'],
                                      sampleset.record.sample)]

        with mock.patch.dict(self.response._result,
                             {'solutions': solutions_without_chain_breaks}):

            # validate data encoding
            self.verify_data_encoding(problem=self.problem,
                                      response=self.response,
                                      solver=self.solver,
                                      params=self.params,
                                      data=data,
                                      embedding_context=self.embedding_context)
Example #6
0
    #'num_spin_reversal_transforms': 2,  # default: 2
}

# schedule = make_reverse_anneal_schedule(s_target=0.99,
#                                        hold_time=1,
#                                        ramp_up_slope=0.2)
#schedule = [(0.0, 1.0), (1.0, 0.7), (7.0, 0.7), (10.0, 1.0)]
#schedule = [(0.0, 1.0), (2.0, 0.7), (8.0, 0.7), (10.0, 1.0)]
schedule = [(0.0, 1.0), (2.0, 0.7), (98.0, 0.7), (100.0, 1.0)]
print("INFO: reverse annealing schedule:")
print(schedule)

neal_sampler = neal.SimulatedAnnealingSampler()

print("INFO: solving initial state")
best_fit = sampler.sample(bqm, **solver_parameters).aggregate().first
energy_bestfit = best_fit.energy
q = np.array(list(best_fit.sample.values()))
y = compact_vector(q, n)
min_hamming = hamming(z_b, q)
best_chi2, best_p = stats.chisquare(y, z, dof)

neal_solution = neal_sampler.sample(bqm, num_reads=num_reads).aggregate().first
neal_energy = neal_solution.energy
neal_q = np.array(list(neal_solution.sample.values()))
neal_y = compact_vector(neal_q, n)
neal_hamm = hamming(z_b, neal_q)

print("INFO: initial solution:", q, "::", y, ":: E=", energy_bestfit)
print("INFO: neal solution:", neal_q, "::", neal_y, ":: E =", neal_energy)
print("INFO: truth value:  ", z_b, "::", z, ":: E =", energy_true_z)
def run_demo():
    # Read the feature-engineered data into a pandas dataframe
    # Data obtained from http://biostat.mc.vanderbilt.edu/DataSets
    demo_path = os.path.dirname(os.path.abspath(__file__))
    data_path = os.path.join(demo_path, 'data', 'formatted_titanic.csv')
    dataset = pd.read_csv(data_path)

    # Rank the MI between survival and every other variable
    scores = {}
    features = list(set(dataset.columns).difference(('survived', )))
    for feature in features:
        scores[feature] = mutual_information(
            prob(dataset[['survived', feature]].values), 0)

    labels, values = zip(
        *sorted(scores.items(), key=lambda pair: pair[1], reverse=True))

    # Plot the MI between survival and every other variable
    plt.figure()
    ax1 = plt.subplot(1, 2, 1)
    ax1.set_title("Mutual Information")
    ax1.set_ylabel('MI Between Survival and Feature')
    plt.xticks(np.arange(len(labels)), labels, rotation=90)
    plt.bar(np.arange(len(labels)), values)

    # The Titanic dataset provides a familiar, intuitive example available in the public
    # domain. In itself, however, it is not a good fit for solving by sampling. Run naively on
    # this dataset, it finds numerous good solutions but is unlikely to find the exact optimal solution.
    # There are many techniques for reformulating problems for the D-Wave system that can
    # improve performance on various metrics, some of which can help narrow down good solutions
    # to closer approach an optimal solution.
    # This demo solves the problem for just the highest-scoring features.

    # Select 8 features with the top MI ranking found above.
    keep = 8

    sorted_scores = sorted(scores.items(),
                           key=lambda pair: pair[1],
                           reverse=True)
    dataset = dataset[[column[0]
                       for column in sorted_scores[0:keep]] + ["survived"]]
    features = list(set(dataset.columns).difference(('survived', )))

    # Build a QUBO that maximizes MI between survival and a subset of features
    bqm = dimod.BinaryQuadraticModel.empty(dimod.BINARY)

    # Add biases as (negative) MI with survival for each feature
    for feature in features:
        mi = mutual_information(prob(dataset[['survived', feature]].values), 1)
        bqm.add_variable(feature, -mi)

    # Add interactions as (negative) MI with survival for each set of 2 features
    for f0, f1 in itertools.combinations(features, 2):
        cmi_01 = conditional_mutual_information(
            prob(dataset[['survived', f0, f1]].values), 1, 2)
        cmi_10 = conditional_mutual_information(
            prob(dataset[['survived', f1, f0]].values), 1, 2)
        bqm.add_interaction(f0, f1, -cmi_01)
        bqm.add_interaction(f1, f0, -cmi_10)

    bqm.normalize()  # Normalize biases & interactions to the range -1, 1

    # Set up a QPU sampler with a fully-connected graph of all the variables
    qpu_sampler = DWaveSampler(solver={'qpu': True})

    embedding = find_clique_embedding(
        bqm.variables,
        16,
        16,
        4,  # size of the chimera lattice
        target_edges=qpu_sampler.edgelist)

    sampler = FixedEmbeddingComposite(qpu_sampler, embedding)

    # For each number of features, k, penalize selection of fewer or more features
    selected_features = np.zeros((len(features), len(features)))
    for k in range(1, len(features) + 1):
        kbqm = bqm.copy()
        kbqm.update(dimod.generators.combinations(
            features, k, strength=6))  # Determines the penalty

        sample = sampler.sample(kbqm, num_reads=10000).first.sample

        for fi, f in enumerate(features):
            selected_features[k - 1, fi] = sample[f]

    # Plot the best feature set per number of selected features
    ax2 = plt.subplot(1, 2, 2)
    ax2.set_title("Best Feature Selection")
    ax2.set_ylabel('Number of Selected Features')
    ax2.set_xticks(np.arange(len(features)))
    ax2.set_xticklabels(features, rotation=90)
    ax2.set_yticks(np.arange(len(features)))
    ax2.set_yticklabels(np.arange(1, len(features) + 1))
    # Set a grid on minor ticks
    ax2.set_xticks(np.arange(-0.5, len(features)), minor=True)
    ax2.set_yticks(np.arange(-0.5, len(features)), minor=True)
    ax2.grid(which='minor', color='black')
    ax2.imshow(selected_features, cmap=colors.ListedColormap(['white', 'red']))

    plots_path = os.path.join(demo_path, "plots.png")
    plt.savefig(plots_path, bbox_inches="tight")
    print("Your plots are saved to {}".format(plots_path))
def main(args):
    num_reads = int(args.nreads)
    dry_run = bool(args.dry_run)
    if dry_run:
        print("WARNING: dry run. There will be no results at the end.")

    # truth-level:
    x = [5, 8, 12, 6, 2]

    # response matrix:
    R = [[1, 2, 0, 0, 0], [1, 2, 1, 1, 0], [0, 1, 3, 2, 0], [0, 2, 2, 3, 2],
         [0, 0, 0, 1, 2]]

    # pseudo-data:
    d = [12, 32, 40, 15, 10]

    # convert to numpy arrays
    x = np.array(x, dtype='uint8')
    R = np.array(R, dtype='uint8')
    b = np.array(d, dtype='uint8')

    # closure test
    b = np.dot(R, x)

    n = 4
    N = x.shape[0]

    print("INFO: N bins:", N)
    print("INFO: n-bits encoding:", n)

    lmbd = np.uint8(args.lmbd)  # regularization strength
    D = laplacian(N)

    # convert to bits
    x_b = discretize_vector(x, n)
    b_b = discretize_vector(b, n)
    R_b = discretize_matrix(R, n)
    D_b = discretize_matrix(D, n)

    print("INFO: Truth-level x:")
    print(x, x_b)
    print("INFO: pseudo-data b:")
    print(b, b_b)
    print("INFO: Response matrix:")
    print(R)
    print(R_b)
    print("INFO: Laplacian operator:")
    print(D)
    print(D_b)
    print("INFO: regularization strength:", lmbd)

    # Create QUBO operator

    # linear constraints
    h = {}
    for j in range(n * N):
        idx = (j)
        h[idx] = 0
        for i in range(N):
            h[idx] += (R_b[i][j] * R_b[i][j] - 2 * R_b[i][j] * b[i] +
                       lmbd * D_b[i][j] * D_b[i][j])

    # quadratic constraints
    J = {}
    for j in range(n * N):
        for k in range(j + 1, n * N):
            idx = (j, k)
            J[idx] = 0
            for i in range(N):
                J[idx] += 2 * (R_b[i][j] * R_b[i][k] +
                               lmbd * D_b[i][j] * D_b[i][k])

    # QUBO
    bqm = dimod.BinaryQuadraticModel(linear=h,
                                     quadratic=J,
                                     offset=0.0,
                                     vartype=dimod.BINARY)
    print("INFO: solving the QUBO model...")
    print("INFO: running on QPU")
    hardware_sampler = DWaveSampler()
    print("INFO: finding optimal minor embedding...")
    embedding = get_embedding_with_short_chain(
        J, tries=1, processor=hardware_sampler.edgelist, verbose=True)
    if embedding is None:
        raise ValueError("ERROR: could not find embedding")

    sampler = FixedEmbeddingComposite(hardware_sampler, embedding)
    print("INFO: creating DWave sampler...")
    print("INFO: annealing (n_reads=%i) ..." % num_reads)
    print("INFO: Connected to", hardware_sampler.properties['chip_id'])
    print("INFO: max anneal schedule points: {}".format(
        hardware_sampler.properties["max_anneal_schedule_points"]))
    print("INFO: annealing time range: {}".format(
        hardware_sampler.properties["annealing_time_range"]))
    schedule = make_reverse_anneal_schedule(s_target=0.99,
                                            hold_time=1,
                                            ramp_up_slope=0.2)
    neal_sampler = neal.SimulatedAnnealingSampler()
    initial_result = sampler.sample(bqm, num_reads=num_reads).aggregate()
    neal_result = neal_sampler.sample(bqm, num_reads=num_reads).aggregate()
    reverse_anneal_params = dict(anneal_schedule=schedule,
                                 initial_state=initial_result.first.sample,
                                 reinitialize_state=True)
    solver_parameters = {
        'num_reads': num_reads,
        'auto_scale': True,
        **reverse_anneal_params
    }

    print(schedule)
    result = sampler.sample(bqm, **solver_parameters)

    reverse_anneal_params = dict(anneal_schedule=schedule,
                                 initial_state=neal_result.first.sample,
                                 reinitialize_state=True)
    solver_parameters = {
        'num_reads': num_reads,
        'auto_scale': True,
        **reverse_anneal_params
    }

    result2 = sampler.sample(bqm, **solver_parameters)
    print("Neal results")
    print_results(neal_result, n)
    print("Reverse annealing on Neal results")
    print_results(result2, n)
    print("QPU results")
    print_results(initial_result, n)
    print("Reverse annealing on QPU results")
    print_results(result, n)
def objective(args):
    lmdb = args['lmbd']
    num_reads = args['num_reads']
    n = args['num_bits']
    annealing_time = args['annealing_time']

    x_b = discretize_vector(x, n)
    z_b = discretize_vector(z, n)
    d_b = discretize_vector(d, n)
    R_b = discretize_matrix(R0, n)
    D_b = discretize_matrix(D, n)

    # Create QUBO operator
    #Q = np.zeros([n*N, n*N])
    S = {}
    h = {}
    J = {}

    # linear constraints
    for j in range(n * N):
        h[(j)] = 0
        for i in range(N):
            h[(j)] += (R_b[i][j] * R_b[i][j] - 2 * R_b[i][j] * d[i] +
                       lmbd * D_b[i][j] * D_b[i][j])
        S[(j, j)] = h[(j)]

    # quadratic constraints
    for j in range(n * N):
        for k in range(j + 1, n * N):
            J[(j, k)] = 0
            for i in range(N):
                J[(j, k)] += 2 * (R_b[i][j] * R_b[i][k] +
                                  lmbd * D_b[i][j] * D_b[i][k])
            S[(j, k)] = J[(j, k)]

    bqm = dimod.BinaryQuadraticModel(linear=h,
                                     quadratic=J,
                                     offset=0.0,
                                     vartype=dimod.BINARY)

    embedding = get_embedding_with_short_chain(
        S, tries=5, processor=hardware_sampler.edgelist, verbose=False)

    sampler = FixedEmbeddingComposite(hardware_sampler, embedding)
    solver_parameters = {
        'num_reads': num_reads,
        'auto_scale': True,
        'annealing_time': annealing_time,  # default: 20 us
        #'anneal_schedule': anneal_sched_custom(id=3),
        'num_spin_reversal_transforms': 2,  # default: 2
    }
    results = sampler.sample(bqm, **solver_parameters).aggregate()
    best_fit = results.first
    energy_bestfit = best_fit.energy
    q = np.array(list(best_fit.sample.values()))
    y = compact_vector(q, n)

    dof = N - 1
    chi2, p = stats.chisquare(y, z, dof)
    chi2dof = chi2 / float(dof)

    hamm = hamming(z_b, q)

    return {
        'loss': hamm,  # chi2dof,
        'status': hp.STATUS_OK,
        'diffxs': y,
        'q': q,
        'hamming': hamm,
        'lmbd': lmbd,
        'num_reads': num_reads,
        'num_bits': n,
        'annealing_time': annealing_time,
    }
Example #10
0
    def solve(self):

        self.n_bins_truth = self._data.x.shape[0]
        self.n_bins_reco = self._data.d.shape[0]

        if not self._data.R.shape[1] == self.n_bins_truth:
            raise Exception(
                "Number of bins at truth level do not match between 1D spectrum (%i) and response matrix (%i)"
                % (self.n_bins_truth, self._data.R.shape[1]))
        if not self._data.R.shape[0] == self.n_bins_reco:
            raise Exception(
                "Number of bins at reco level do not match between 1D spectrum (%i) and response matrix (%i)"
                % (self.n_bins_reco, self._data.R.shape[0]))

        self.convert_to_binary()

        print("INFO: N bins:", self._data.x.shape[0])
        print("INFO: n-bits encoding:", self.rho)

        print("INFO: Signal truth-level x:")
        print(self._data.x)
        print("INFO: pseudo-data b:")
        print(self._data.d)
        print("INFO: Response matrix:")
        print(self._data.R)

        self.Q = self.make_qubo_matrix()
        self._bqm = dimod.BinaryQuadraticModel.from_numpy_matrix(self.Q)

        print("INFO: solving the QUBO model (size=%i)..." % len(self._bqm))

        if self.backend in [Backends.cpu]:
            print("INFO: running on CPU...")
            self._results = dimod.ExactSolver().sample(self._bqm)
            self._status = StatusCode.success

        elif self.backend in [Backends.sim]:
            num_reads = self.solver_parameters['num_reads']
            print("INFO: running on simulated annealer (neal), num_reads=",
                  num_reads)

            sampler = neal.SimulatedAnnealingSampler()
            self._results = sampler.sample(self._bqm,
                                           num_reads=num_reads).aggregate()
            self._status = StatusCode.success

        elif self.backend in [
                Backends.qpu, Backends.qpu_hinoise, Backends.qpu_lonoise,
                Backends.hyb, Backends.qsolv
        ]:
            print("INFO: running on QPU")

            config_file = self.get_config_file()
            self._hardware_sampler = DWaveSampler(config_file=config_file)
            print("INFO: QPU configuration file:", config_file)

            print("INFO: finding optimal minor embedding...")

            n_bits_avg = np.mean(self._encoder.rho)
            thr = 4. / float(self.n_bins_truth)
            n_tries = 5 if n_bits_avg < thr else 10

            J = qubo_quadratic_terms_from_np_array(self.Q)
            embedding = self.find_embedding(J, n_tries)

            print("INFO: creating DWave sampler...")
            sampler = FixedEmbeddingComposite(self._hardware_sampler,
                                              embedding)

            if self.backend in [
                    Backends.qpu, Backends.qpu_hinoise, Backends.qpu_lonoise
            ]:
                print("INFO: Running on QPU")
                params = self.solver_parameters
                self._results = sampler.sample(self._bqm, **params).aggregate()
                self._status = StatusCode.success

            elif self.backend in [Backends.hyb]:
                print("INFO: hybrid execution")
                import hybrid

                num_reads = self.solver_parameters['num_reads']
                # Define the workflow
                # hybrid.EnergyImpactDecomposer(size=len(bqm), rolling_history=0.15)
                iteration = hybrid.RacingBranches(
                    hybrid.InterruptableTabuSampler(),
                    hybrid.EnergyImpactDecomposer(size=len(self._bqm) // 2,
                                                  rolling=True)
                    | hybrid.QPUSubproblemAutoEmbeddingSampler(
                        num_reads=num_reads)
                    | hybrid.SplatComposer()) | hybrid.ArgMin()
                #workflow = hybrid.LoopUntilNoImprovement(iteration, convergence=3)
                workflow = hybrid.Loop(iteration, max_iter=20, convergence=3)

                init_state = hybrid.State.from_problem(self._bqm)
                self._results = workflow.run(init_state).result().samples
                self._status = StatusCode.success

                # show execution profile
                print("INFO: timing:")
                workflow.timers
                hybrid.print_structure(workflow)
                hybrid.profiling.print_counters(workflow)

            elif self.backend in [Backends.qsolv]:
                print("INFO: using QBsolve with FixedEmbeddingComposite")
                self._results = QBSolv().sample_qubo(S,
                                                     solver=sampler,
                                                     solver_limit=5)
                self._status = StatusCode.success

            else:
                raise Exception("ERROR: unknown backend", self.backend)

        print("DEBUG: status =", self._status)
        return self._status