def ParallelTempering(num_sweeps=10000, num_replicas=10, max_iter=None, max_time=None, convergence=3): """Parallel tempering workflow generator. Args: num_sweeps (int, optional): Number of sweeps in the fixed temperature sampling. num_replicas (int, optional): Number of replicas (parallel states / workflow branches). max_iter (int/None, optional): Maximum number of iterations of the update/swaps loop. max_time (int/None, optional): Maximum wall clock runtime (in seconds) allowed in the update/swaps loop. convergence (int/None, optional): Number of times best energy of the coldest replica has to repeat before we terminate. Returns: Workflow (:class:`~hybrid.core.Runnable` instance). """ # expand single input state into `num_replicas` replica states preprocess = SpawnParallelTemperingReplicas(num_replicas=num_replicas) # fixed temperature sampling on all replicas in parallel update = hybrid.Map(FixedTemperatureSampler(num_sweeps=num_sweeps)) # replica exchange step: do the top-down sweep over adjacent pairs # (good hot samples sink to bottom) swap = SwapReplicasDownsweep() # loop termination key function def key(states): if states is not None: return states[-1].samples.first.energy # replicas update/swap until Loop termination criteria reached loop = hybrid.Loop(update | swap, max_iter=max_iter, max_time=max_time, convergence=convergence, key=key) # collapse all replicas (although the bottom one should be the best) postprocess = hybrid.MergeSamples(aggregate=True) workflow = preprocess | loop | postprocess return workflow
subproblem_size = 100 print("BQM size: {}, subproblem size: {}".format(len(bqm), subproblem_size)) # Classical solvers #subproblem = hybrid.EnergyImpactDecomposer(size=50, rolling_history=0.15) #subproblem = hybrid.EnergyImpactDecomposer(size=1024, rolling_history=0.15, traversal="bfs") # Parallel subproblem subproblem = hybrid.Unwind( #hybrid.EnergyImpactDecomposer(size=subproblem_size, rolling_history=0.15, traversal="bfs") hybrid.EnergyImpactDecomposer(size=subproblem_size, rolling_history=0.1)) # QPU #subsampler = hybrid.QPUSubproblemAutoEmbeddingSampler() | hybrid.SplatComposer() subsampler = hybrid.Map( hybrid.QPUSubproblemAutoEmbeddingSampler()) | hybrid.Reduce( hybrid.Lambda(merge_substates)) | hybrid.SplatComposer() # Define the workflow # iteration = hybrid.RacingBranches( # hybrid.InterruptableTabuSampler(), # hybrid.EnergyImpactDecomposer(size=2) # | hybrid.QPUSubproblemAutoEmbeddingSampler() # | hybrid.SplatComposer() # ) | hybrid.ArgMin() #iteration = hybrid.RacingBranches( iteration = hybrid.Race( hybrid.InterruptableTabuSampler(), #hybrid.SimulatedAnnealingProblemSampler(), subproblem | subsampler) | hybrid.ArgMin()
n_replicas = 10 n_iterations = 10 # states are randomly initialized state = hybrid.State.from_problem(bqm) # get a reasonable beta range beta_hot, beta_cold = neal.default_beta_range(bqm) # generate betas for all branches/replicas betas = np.geomspace(beta_hot, beta_cold, n_replicas) # create n_replicas with geometric distribution of betas (inverse temperature) replicas = hybrid.States(*[state.updated(beta=b) for b in betas]) # run replicas update/swap for n_iterations # (after each update/sampling step, do n_replicas-1 swap operations) update = hybrid.Map(FixedTemperatureSampler(num_sweeps=n_sweeps)) swap = SwapReplicasDownsweep() workflow = hybrid.Loop(update | swap, max_iter=n_iterations) \ | hybrid.MergeSamples(aggregate=True) solution = workflow.run(replicas).result() # show execution profile hybrid.profiling.print_counters(workflow) # show results print("Solution: sample={0.samples.first}, energy={0.samples.first.energy}". format(solution))
problem = sys.argv[1] with open(problem) as fp: bqm = dimod.BinaryQuadraticModel.from_coo(fp) # define a qbsolv-like workflow def merge_substates(_, substates): a, b = substates return a.updated( subsamples=hybrid.hstack_samplesets(a.subsamples, b.subsamples)) subproblems = hybrid.Unwind( hybrid.EnergyImpactDecomposer(size=50, rolling_history=0.15)) qpu = hybrid.Map(hybrid.QPUSubproblemAutoEmbeddingSampler()) | hybrid.Reduce( hybrid.Lambda(merge_substates)) | hybrid.SplatComposer() random = hybrid.Map(hybrid.RandomSubproblemSampler()) | hybrid.Reduce( hybrid.Lambda(merge_substates)) | hybrid.SplatComposer() subsampler = hybrid.Parallel(qpu, random, endomorphic=False) | hybrid.ArgMin() iteration = hybrid.Race(hybrid.InterruptableTabuSampler(), subproblems | subsampler) | hybrid.ArgMin() main = hybrid.Loop(iteration, max_iter=10, convergence=3) # run the workflow init_state = hybrid.State.from_sample(hybrid.min_sample(bqm), bqm) solution = main.run(init_state).result()