def HybridizedPopulationAnnealing(num_reads=20, num_iter=20, num_sweeps=1000): """Workflow generator for population annealing initialized with QPU samples. Args: num_reads (int): Size of the population of samples. num_iter (int): Number of temperatures over which we iterate fixed-temperature sampling / resampling. num_sweeps (int): Number of sweeps in the fixed temperature sampling step. Returns: Workflow (:class:`~hybrid.core.Runnable` instance). """ # QPU initial sampling: limits the PA workflow to QPU-sized problems qpu_init = (hybrid.IdentityDecomposer() | hybrid.QPUSubproblemAutoEmbeddingSampler(num_reads=num_reads) | hybrid.IdentityComposer()) | hybrid.AggregatedSamples(False) # PA workflow: after initial QPU sampling and initial beta schedule estimation, # we do `num_iter` steps (one per beta/temperature) of fixed-temperature # sampling / weighted resampling workflow = qpu_init | CalculateAnnealingBetaSchedule( length=num_iter) | hybrid.Loop( ProgressBetaAlongSchedule() | hybrid.FixedTemperatureSampler(num_sweeps=num_sweeps) | EnergyWeightedResampler(), max_iter=num_iter) return workflow
def HybridizedPopulationAnnealing(num_reads=100, num_iter=100, num_sweeps=100, beta_range=None): """Workflow generator for population annealing initialized with QPU samples. Args: num_reads (int): Size of the population of samples. num_iter (int): Number of temperatures over which we iterate fixed-temperature sampling / resampling. num_sweeps (int): Number of sweeps in the fixed temperature sampling step. beta_range (tuple[float], optional): A 2-tuple defining the beginning and end of the beta schedule, where beta is the inverse temperature. Passed to :class:`.CalculateAnnealingBetaSchedule` for linear schedule generation. Returns: Workflow (:class:`~hybrid.core.Runnable` instance). """ # QPU initial sampling: limits the PA workflow to QPU-sized problems qpu_init = (hybrid.IdentityDecomposer() | hybrid.QPUSubproblemAutoEmbeddingSampler(num_reads=num_reads) | hybrid.IdentityComposer()) | hybrid.AggregatedSamples(False) # PA workflow: after initial QPU sampling and initial beta schedule estimation, # we do `num_iter` steps (one per beta/temperature) of fixed-temperature # sampling / weighted resampling schedule_init = CalculateAnnealingBetaSchedule(length=num_iter, beta_range=beta_range, interpolation='linear') workflow = qpu_init | schedule_init | hybrid.Loop( ProgressBetaAlongSchedule() | hybrid.FixedTemperatureSampler(num_sweeps=num_sweeps) | EnergyWeightedResampler(), max_iter=num_iter) return workflow
print("BQM: {} nodes, {} edges, {:.2f} density".format( len(bqm), len(bqm.quadratic), hybrid.bqm_density(bqm))) # sweeps per fixed-temperature sampling step num_sweeps = 1000 # number of generations, or temperatures to progress through num_iter = 20 # population size num_samples = 20 # QPU initial sampling: limits the PA workflow to QPU-sized problems qpu_init = (hybrid.IdentityDecomposer() | hybrid.QPUSubproblemAutoEmbeddingSampler(num_reads=num_samples) | hybrid.IdentityComposer()) | hybrid.AggregatedSamples(False) # PA workflow: after initial beta schedule estimation, we do `num_iter` steps # (one per beta/temperature) of fixed-temperature sampling / weighted resampling workflow = qpu_init | CalculateAnnealingBetaSchedule( length=num_iter) | hybrid.Loop( ProgressBetaAlongSchedule() | FixedTemperatureSampler( num_sweeps=num_sweeps) | EnergyWeightedResampler(), max_iter=num_iter) # run the workflow state = hybrid.State.from_problem(bqm) solution = workflow.run(state).result() # show execution profile hybrid.profiling.print_counters(workflow)
n_replicas = 10 n_iterations = 10 # replicas are initialized with random samples state = hybrid.State.from_problem(bqm) replicas = hybrid.States(*[state.updated() for _ in range(n_replicas)]) # 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) # QPU branch: limits the PT workflow to QPU-sized problems qpu = hybrid.IdentityDecomposer() | hybrid.QPUSubproblemAutoEmbeddingSampler( ) | hybrid.IdentityComposer() # use QPU as the hottest temperature sampler and `n_replicas-1` fixed-temperature-samplers update = hybrid.Branches( qpu, *[ FixedTemperatureSampler(beta=beta, num_sweeps=n_sweeps) for beta in betas[1:] ]) # swap step is `n_replicas-1` pairwise potential swaps swap = SwapReplicasDownsweep(betas=betas) # we'll run update/swap sequence for `n_iterations` workflow = hybrid.Loop(update | swap, max_iter=n_iterations) \ | hybrid.MergeSamples(aggregate=True)
n_sweeps = 10000 n_replicas = 10 n_iterations = 10 # replicas are initialized with random samples state = hybrid.State.from_problem(bqm) replicas = hybrid.States(*[state.updated() for _ in range(n_replicas)]) # 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) # QPU branch: limits the PT workflow to QPU-sized problems qpu = hybrid.IdentityDecomposer() | hybrid.QPUSubproblemAutoEmbeddingSampler() | hybrid.IdentityComposer() # use QPU as the hottest temperature sampler and `n_replicas-1` fixed-temperature-samplers update = hybrid.Branches( qpu, *[FixedTemperatureSampler(beta, num_sweeps=n_sweeps) for beta in betas[1:]]) # swap step is `n_replicas-1` pairwise potential swaps swap = SwapReplicasSweepDown(betas) # we'll run update/swap sequence for `n_iterations` workflow = hybrid.Loop(update | swap, max_iter=n_iterations) \ | hybrid.MergeSamples(aggregate=True) # execute the workflow solution = workflow.run(replicas).result()
def HybridizedParallelTempering(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) # QPU branch: limits the PT workflow to QPU-sized problems qpu = (hybrid.IdentityDecomposer() | hybrid.QPUSubproblemAutoEmbeddingSampler() | hybrid.IdentityComposer()) # use QPU as the hottest temperature sampler and `num_replicas-1` fixed-temperature-samplers update = hybrid.Branches( qpu, *[ FixedTemperatureSampler(num_sweeps=num_sweeps) for _ in range(num_replicas - 1) ]) # 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