예제 #1
0
 def test_pfs_traverse_connected_some_visited(self):
     eid = EnergyImpactDecomposer(size=None, traversal='pfs')
     bqm = dimod.BinaryQuadraticModel({}, {'ab': 1, 'bc': 1, 'cd': 1, 'da': 1, 'bd': 1}, 0.0, dimod.SPIN)
     priority = collections.OrderedDict(zip('bdac', itertools.count(0, -1)))
     var = eid.traverse(bqm=bqm, sample=None, ordered_priority=priority, visited=set('b'), size=3)
     # start with 'b' visited, so use 'd' as root and pick 2 more neighbors
     self.assertEqual(var, set('dac'))
예제 #2
0
 def test_pfs_traverse_connected_too_small(self):
     eid = EnergyImpactDecomposer(size=None, traversal='pfs')
     bqm = dimod.BinaryQuadraticModel({}, {'ab': 1, 'bc': 1, 'cd': 1, 'da': 1}, 0.0, dimod.SPIN)
     priority = collections.OrderedDict(zip('bdac', itertools.count(0, -1)))
     var = eid.traverse(bqm=bqm, sample=None, ordered_priority=priority, visited=set(), size=5)
     # start from 'b', try to pick more then there is (get complete graph back)
     self.assertEqual(var, set('abcd'))
예제 #3
0
 def test_pfs_traverse_disconnected_some_visited(self):
     eid = EnergyImpactDecomposer(size=None, traversal='pfs')
     bqm = dimod.BinaryQuadraticModel({}, {'ab': 1, 'bc': 1, 'cd': 1, 'da': 1, 'ef': 1, 'fg': 1}, 0.0, dimod.SPIN)
     priority = collections.OrderedDict(zip('abcdefg', itertools.count(0, -1)))
     var = eid.traverse(bqm=bqm, sample=None, ordered_priority=priority, visited=set('abc'), size=3)
     # pick 'd' from first graph component, and 'ef' from the second
     self.assertEqual(var, set('def'))
예제 #4
0
 def test_pfs_traverse_connected(self):
     eid = EnergyImpactDecomposer(size=None, traversal='pfs')
     bqm = dimod.BinaryQuadraticModel({}, {'ab': 1, 'bc': 1, 'cd': 1, 'da': 1}, 0.0, dimod.SPIN)
     priority = collections.OrderedDict(zip('bdac', itertools.count(0, -1)))
     var = eid.traverse(bqm=bqm, sample=None, ordered_priority=priority, visited=set(), size=3)
     # start from 'b', pick 2 connected variables, traversed in order of best energy (priority)
     self.assertEqual(var, set('bad'))
예제 #5
0
    def test_multi_vars(self):
        """Multiple variables subproblem selection works, without gain limit."""

        state = State.from_sample({'a': 1, 'b': 1, 'c': -1}, self.notall)
        eid = EnergyImpactDecomposer(size=3, min_gain=None)
        nextstate = eid.next(state)
        self.assertDictEqual(dict(nextstate.subproblem.adj), dict(self.notall.adj))
    def test_no_vars(self):
        """Failure due to no sub vars available."""

        state = State.from_sample({'a': 1, 'b': 1, 'c': -1}, self.notall)
        eid = EnergyImpactDecomposer(max_size=3, min_gain=5.0)
        with self.assertRaises(ValueError):
            nextstate = eid.next(state)
예제 #7
0
    def test_pfs_on_impactful_far_subproblem(self):
        # problem graph has two components, each one is 4-node cycle graph
        # variable flip energy gains order variables: a, h..b
        edges = {
            'ab': 1,
            'bc': 1,
            'cd': 1,
            'da': 1,
            'ef': 1,
            'fg': 1,
            'gh': 1,
            'he': 1,
            'de': 0
        }
        biases = dict(zip(string.ascii_letters, range(8)))
        biases['a'] += 10
        bqm = dimod.BinaryQuadraticModel(biases, edges, 0.0, 'SPIN')
        sample = {i: -1 for i in bqm.variables}

        state = State.from_sample(sample, bqm)
        eid = EnergyImpactDecomposer(size=5, traversal='pfs')
        result = eid.run(state).result()

        # move towards second cluster and pick the highest energy variables from there
        self.assertEqual(set(result.subproblem.variables), set('adehg'))
예제 #8
0
    def test_no_vars(self):
        """Failure due to no sub vars available."""

        state = State.from_sample({'a': 1, 'b': 1, 'c': -1}, self.notall)
        eid = EnergyImpactDecomposer(size=3, min_gain=5.0)
        nextstate = eid.next(state)
        self.assertEqual(len(nextstate.subproblem), 0)
예제 #9
0
    def test_rolling_subproblem_larger_than_rolling_history(self):
        """In case rolling history too small, just one problem is unrolled."""

        # 10 variables, 0 to 9 when ordered by energy increase on flip
        bqm = dimod.BinaryQuadraticModel({i: i for i in range(10)}, {}, 0.0, 'SPIN')
        sample = {i: 1 for i in range(10)}

        # exactly 1 five-variable problems should be produced
        state = State.from_sample(sample, bqm)
        eid = EnergyImpactDecomposer(size=5, rolling=True, rolling_history=0.3, silent_rewind=False)
        states = list(iter(partial(eid.next, state=state), None))

        self.assertEqual(len(states), 1)
        self.assertEqual(len(states[0].subproblem), 5)
        self.assertEqual(list(dict(states[0].subproblem.linear).values()), list(range(0,5)))

        # works even for subproblems as large as the input problem
        eid = EnergyImpactDecomposer(size=len(bqm), rolling=True, rolling_history=0.3, silent_rewind=False)
        states = list(iter(partial(eid.next, state=state), None))

        self.assertEqual(len(states), 1)
        self.assertEqual(len(states[0].subproblem), 10)
        self.assertEqual(list(dict(states[0].subproblem.linear).values()), list(range(0,10)))

        # but adapt to problem size if subproblem larger than problem
        eid = EnergyImpactDecomposer(size=11, rolling=True, rolling_history=0.3, silent_rewind=False)
        states = list(iter(partial(eid.next, state=state), None))

        self.assertEqual(len(states), 1)
        self.assertEqual(len(states[0].subproblem), 10)
        self.assertEqual(list(dict(states[0].subproblem.linear).values()), list(range(0,10)))
예제 #10
0
    def test_adaptive_vars(self):
        """Multiple variables subproblem selection works, with gain limit."""

        state = State.from_sample({'a': 1, 'b': 1, 'c': -1}, self.notall)
        eid = EnergyImpactDecomposer(size=3, min_gain=2.0)
        nextstate = eid.next(state)
        self.assertDictEqual(dict(nextstate.subproblem.linear), {'c': 2})
        self.assertDictEqual(dict(nextstate.subproblem.quadratic), {})
예제 #11
0
    def test_one_var(self):
        """First-variable selection works."""

        state = State.from_sample({'a': 1, 'b': 1, 'c': -1}, self.notall)
        eid = EnergyImpactDecomposer(size=1, min_gain=0)
        nextstate = eid.next(state)
        self.assertDictEqual(dict(nextstate.subproblem.linear), {'c': 2})
        self.assertDictEqual(dict(nextstate.subproblem.quadratic), {})
예제 #12
0
 def test_energy_traverse(self):
     eid = EnergyImpactDecomposer(size=1, traversal='energy')
     priority = collections.OrderedDict((v, v) for v in range(1, 4))
     var = eid.traverse(bqm=None,
                        sample=None,
                        ordered_priority=priority,
                        visited=[2, 1],
                        size=2)
     self.assertEqual(var, [3])
예제 #13
0
    def test_bfs_on_sequential_eid_calls_over_disconnected_problem_graph(self):
        # problem graph has two components, each one is 4-node cycle graph
        edges = {'ab': 1, 'bc': 1, 'cd': 1, 'da': 1,
                 'ef': 1, 'fg': 1, 'gh': 1, 'he': 1}
        biases = dict(zip(string.ascii_letters, range(8, 0, -1)))
        bqm = dimod.BinaryQuadraticModel(biases, edges, 0.0, 'SPIN')
        sample = {i: -1 for i in bqm.variables}

        state = State.from_sample(sample, bqm)
        eid = EnergyImpactDecomposer(size=3, traversal='bfs', rolling=True,
                                     rolling_history=1.0, silent_rewind=False)
        states = list(iter(partial(eid.next, state=state), None))

        # energy impact list is: [a..h], so of the 3 subproblems generated,
        # the middle one is disconnected with one var from first group and two
        # variables from the second

        # `c` has higher energy, but it's not connected to `a`, so `d` is picked
        self.assertEqual(set(states[0].subproblem.variables), set('abd'))

        # `c` is picked from the first component, and the seed for the next
        # subproblem is `e`. however, the order of `e`'s neighbors is not defined,
        # so if we need to pick just one, it could be `f` or `h`
        # (note: PFS has a defined order of neighbors)
        self.assertTrue(set('cefh').difference(states[1].subproblem.variables).issubset('fh'))
        self.assertEqual(len(states[1].subproblem.variables), 3)

        # the second component is exhausted in search for 3 variable subproblem
        third = set(states[2].subproblem.variables)
        self.assertTrue(third == set('gh') or third == set('gf'))
예제 #14
0
    def test_pfs_on_sequential_eid_calls_over_disconnected_problem_graph(self):
        # problem graph has two components, each one is 4-node cycle graph
        edges = {'ab': 1, 'bc': 1, 'cd': 1, 'da': 1,
                 'ef': 1, 'fg': 1, 'gh': 1, 'he': 1}
        biases = dict(zip(string.ascii_letters, range(8, 0, -1)))
        bqm = dimod.BinaryQuadraticModel(biases, edges, 0.0, 'SPIN')
        sample = {i: -1 for i in bqm.variables}

        state = State.from_sample(sample, bqm)
        eid = EnergyImpactDecomposer(size=3, traversal='pfs', rolling=True,
                                     rolling_history=1.0, silent_rewind=False)
        states = list(iter(partial(eid.next, state=state), None))

        # energy impact list is: [a..h], so of the 3 subproblems generated,
        # the middle one is disconnected with one var from first group and two
        # variables from the second

        # pfs is seeded with `a` and connected nodes in order of energy are picked
        self.assertEqual(set(states[0].subproblem.variables), set('abc'))

        # `d` left from the first component, and the seed for the next
        # subproblem is the next highest in energy `e`.
        # unlike in bfs, the order of `e`'s neighbors is well defined
        self.assertEqual(set(states[1].subproblem.variables), set('def'))

        # the second component is exhausted in search for 3 variable subproblem
        self.assertEqual(set(states[2].subproblem.variables), set('gh'))
예제 #15
0
    def test_rolling_subproblem(self):
        """Selected number of (non-overlapping) subproblems are unrolled from the input problem."""

        # 10 variables, 0 to 9 when ordered by energy increase on flip
        bqm = dimod.BinaryQuadraticModel({i: i for i in range(10)}, {}, 0.0, 'SPIN')
        sample = {i: 1 for i in range(10)}

        # exactly 5 single-variable problems should be produced
        state = State.from_sample(sample, bqm)
        eid = EnergyImpactDecomposer(size=1, rolling=True, rolling_history=0.5, silent_rewind=False)
        states = list(iter(partial(eid.next, state=state), None))

        self.assertEqual(len(states), 5)
        for idx, state in enumerate(states):
            self.assertEqual(state.subproblem.linear[idx], idx)
예제 #16
0
import dimod

from hybrid.samplers import (QPUSubproblemAutoEmbeddingSampler,
                             InterruptableTabuSampler)
from hybrid.decomposers import EnergyImpactDecomposer
from hybrid.composers import SplatComposer
from hybrid.core import State
from hybrid.flow import RacingBranches, ArgMin, Loop
from hybrid.utils import min_sample

# load a problem
problem = sys.argv[1]
with open(problem) as fp:
    bqm = dimod.BinaryQuadraticModel.from_coo(fp)

# define the solver
iteration = RacingBranches(
    InterruptableTabuSampler(),
    EnergyImpactDecomposer(max_size=50, rolling=True, rolling_history=0.15)
    | QPUSubproblemAutoEmbeddingSampler()
    | SplatComposer()) | ArgMin()
main = Loop(iteration, max_iter=10, convergence=3)

# run solver
init_state = State.from_sample(min_sample(bqm), bqm)
solution = main.run(init_state).result()

# show results
print("Solution: sample={s.samples.first}".format(s=solution))
예제 #17
0
import dimod

from hybrid.samplers import (QPUSubproblemAutoEmbeddingSampler,
                             TabuProblemSampler, InterruptableTabuSampler)
from hybrid.decomposers import EnergyImpactDecomposer
from hybrid.composers import SplatComposer
from hybrid.core import State, SampleSet
from hybrid.flow import RacingBranches, ArgMin, Loop
from hybrid.utils import min_sample, max_sample, random_sample

problem = sys.argv[1]
with open(problem) as fp:
    bqm = dimod.BinaryQuadraticModel.from_coo(fp)

# Run Tabu in parallel with QPU, but post-process QPU samples with very short Tabu
iteration = RacingBranches(
    InterruptableTabuSampler(),
    EnergyImpactDecomposer(size=50)
    | QPUSubproblemAutoEmbeddingSampler(num_reads=100)
    | SplatComposer()
    | TabuProblemSampler(timeout=1)) | ArgMin()

main = Loop(iteration, max_iter=10, convergence=3)

init_state = State.from_sample(min_sample(bqm), bqm)

solution = main.run(init_state).result()

print("Solution: energy={s.samples.first.energy}".format(s=solution))
예제 #18
0
from hybrid.samplers import (SimulatedAnnealingSubproblemSampler,
                             TabuSubproblemSampler, InterruptableTabuSampler)
from hybrid.decomposers import EnergyImpactDecomposer, IdentityDecomposer
from hybrid.composers import SplatComposer
from hybrid.core import State
from hybrid.flow import RacingBranches, ArgMin, Loop
from hybrid.utils import min_sample

problem = sys.argv[1]
with open(problem) as fp:
    bqm = dimod.BinaryQuadraticModel.from_coo(fp)

iteration = RacingBranches(
    IdentityDecomposer() | SimulatedAnnealingSubproblemSampler()
    | SplatComposer(),
    EnergyImpactDecomposer(max_size=50)
    | RacingBranches(SimulatedAnnealingSubproblemSampler(sweeps=1000),
                     TabuSubproblemSampler(tenure=20, timeout=10),
                     endomorphic=False)
    | ArgMin(operator.attrgetter('subsamples.first.energy'))
    | SplatComposer()) | ArgMin()

main = Loop(iteration, max_iter=10, convergence=3)

init_state = State.from_sample(min_sample(bqm), bqm)

solution = main.run(init_state).result()

print("Solution: energy={s.samples.first.energy}".format(s=solution))
예제 #19
0
    def sample(self,
               bqm,
               init_sample=None,
               max_iter=100,
               convergence=10,
               num_reads=1,
               sa_reads=1,
               sa_sweeps=1000,
               qpu_reads=100,
               qpu_sampler=None,
               max_subproblem_size=50):
        """Run Tabu search, Simulated annealing and QPU subproblem sampling (for
        high energy impact problem variables) in parallel and return the best
        samples.

        Args:
            bqm (:obj:`~dimod.BinaryQuadraticModel`):
                Binary quadratic model to be sampled from.

            init_sample (:class:`~dimod.SampleSet`, callable, ``None``):
                Initial sample set (or sample generator) used for each "read".
                Use a random sample for each read by default.

            max_iter (int):
                Number of iterations in the hybrid algorithm.

            convergence (int):
                Number of iterations with no improvement that terminates sampling.

            num_reads (int):
                Number of reads. Each sample is the result of a single run of the
                hybrid algorithm.

            sa_reads (int):
                Number of reads in the simulated annealing branch.

            sa_sweeps (int):
                Number of sweeps in the simulated annealing branch.

            qpu_reads (int):
                Number of reads in the QPU branch.

            qpu_sampler (:class:`dimod.Sampler`, optional, default=DWaveSampler()):
                Quantum sampler such as a D-Wave system.

            max_subproblem_size (int):
                Maximum size of the subproblem selected in the QPU branch.

        Returns:
            :obj:`~dimod.SampleSet`: A `dimod` :obj:`.~dimod.SampleSet` object.

        """

        if callable(init_sample):
            init_state_gen = lambda: State.from_sample(init_sample(), bqm)
        elif init_sample is None:
            init_state_gen = lambda: State.from_sample(random_sample(bqm), bqm)
        elif isinstance(init_sample, dimod.SampleSet):
            init_state_gen = lambda: State.from_sample(init_sample, bqm)
        else:
            raise TypeError(
                "'init_sample' should be a SampleSet or a SampleSet generator")

        subproblem_size = min(len(bqm), max_subproblem_size)

        iteration = RacingBranches(
            InterruptableTabuSampler(),
            InterruptableSimulatedAnnealingProblemSampler(num_reads=sa_reads,
                                                          sweeps=sa_sweeps),
            EnergyImpactDecomposer(size=subproblem_size,
                                   rolling=True,
                                   rolling_history=0.3,
                                   traversal='bfs')
            | QPUSubproblemAutoEmbeddingSampler(num_reads=qpu_reads,
                                                qpu_sampler=qpu_sampler)
            | SplatComposer(),
        ) | ArgMin()
        self.runnable = Loop(iteration,
                             max_iter=max_iter,
                             convergence=convergence)

        samples = []
        energies = []
        for _ in range(num_reads):
            init_state = init_state_gen()
            final_state = self.runnable.run(init_state)
            # the best sample from each run is one "read"
            ss = final_state.result().samples
            ss.change_vartype(bqm.vartype, inplace=True)
            samples.append(ss.first.sample)
            energies.append(ss.first.energy)

        return dimod.SampleSet.from_samples(samples,
                                            vartype=bqm.vartype,
                                            energy=energies)
예제 #20
0
from hybrid.samplers import (SimulatedAnnealingSubproblemSampler,
                             TabuSubproblemSampler, InterruptableTabuSampler)
from hybrid.decomposers import EnergyImpactDecomposer, IdentityDecomposer
from hybrid.composers import SplatComposer
from hybrid.core import State
from hybrid.flow import RacingBranches, ArgMin, Loop
from hybrid.utils import min_sample

problem = sys.argv[1]
with open(problem) as fp:
    bqm = dimod.BinaryQuadraticModel.from_coo(fp)

iteration = RacingBranches(
    IdentityDecomposer() | SimulatedAnnealingSubproblemSampler()
    | SplatComposer(),
    EnergyImpactDecomposer(max_size=50, min_diff=50)
    | RacingBranches(SimulatedAnnealingSubproblemSampler(sweeps=1000),
                     TabuSubproblemSampler(tenure=20, timeout=10),
                     endomorphic=False)
    | ArgMin(operator.attrgetter('subsamples.first.energy'))
    | SplatComposer()) | ArgMin()

main = Loop(iteration, max_iter=10, convergence=3)

init_state = State.from_sample(min_sample(bqm), bqm)

solution = main.run(init_state).result()

print("Solution: energy={s.samples.first.energy}".format(s=solution))