def test_min_gap_with_aux(self):
        """Verify min_classical_gap parameter works
        """
        decision_variables = ['a', 'b', 'c']
        xor_gate = {(-1, -1, -1): 0,
                    (-1, 1, 1): 0,
                    (1, -1, 1): 0,
                    (1, 1, -1): 0}
        graph = nx.complete_graph(decision_variables + ['aux0'])

        linear_energy_ranges = {v: (-2., 2.) for v in graph}
        quadratic_energy_ranges = {(u, v): (-1., 1.) for u, v in graph.edges}

        # Run problem with a min_classical_gap that is too large
        with self.assertRaises(pm.ImpossiblePenaltyModel):
            large_min_gap = 3
            maxgap.generate(graph, xor_gate, decision_variables,
                            linear_energy_ranges,
                            quadratic_energy_ranges,
                            large_min_gap,
                            None)

        # Lowering min_classical_gap should lead to a bqm being found
        min_classical_gap = .5
        self.generate_and_check(graph, xor_gate, decision_variables,
                                linear_energy_ranges,
                                quadratic_energy_ranges,
                                min_classical_gap)
    def test_impossible(self):
        graph = nx.path_graph(3)
        configurations = {(-1, -1, -1): 0,
                          (-1, +1, -1): 0,
                          (+1, -1, -1): 0,
                          (+1, +1, +1): 0}
        decision_variables = (0, 1, 2)
        linear_energy_ranges = {v: (-2., 2.) for v in graph}
        quadratic_energy_ranges = {(u, v): (-1., 1.) for u, v in graph.edges}
        min_classical_gap = 2

        with self.assertRaises(pm.ImpossiblePenaltyModel):
            maxgap.generate(graph, configurations, decision_variables,
                            linear_energy_ranges,
                            quadratic_energy_ranges,
                            min_classical_gap)
    def test_negative_min_gap_impossible_bqm(self):
        """XOR Gate problem without auxiliary variables
        Note: Regardless of the negative gap, this BQM should remain impossible.
        """
        negative_gap = -3
        decision_variables = ['a', 'b', 'c']
        xor_gate = {(-1, -1, -1): 0,
                    (-1, 1, 1): 0,
                    (1, -1, 1): 0,
                    (1, 1, -1): 0}
        graph = nx.complete_graph(decision_variables)

        linear_energy_ranges = {v: (-2., 2.) for v in graph}
        quadratic_energy_ranges = {(u, v): (-1., 1.) for u, v in graph.edges}

        with self.assertRaises(pm.ImpossiblePenaltyModel):
            maxgap.generate(graph, xor_gate, decision_variables,
                            linear_energy_ranges,
                            quadratic_energy_ranges,
                            negative_gap,
                            None)
Beispiel #4
0
    def test_min_gap_equals_max_gap(self):
        # Make sure that a model is always grabbed, even when min_gap == max_gap
        min_gap = 4  # This value is also the max classical gap
        decision_variables = ['a']
        config = {(-1, ): -1}
        graph = nx.complete_graph(decision_variables)

        linear_energy_ranges = {v: (-2., 2.) for v in graph}
        quadratic_energy_ranges = {(u, v): (-1., 1.) for u, v in graph.edges}

        bqm, gap = maxgap.generate(graph, config, decision_variables,
                                   linear_energy_ranges,
                                   quadratic_energy_ranges, min_gap, None)

        # Check that a model was found
        self.assertIsNotNone(bqm.linear)
        self.assertIsNotNone(bqm.quadratic)
        self.assertIsNotNone(bqm.offset)
        self.assertEqual(
            min_gap, gap)  # Min gap is also the max classical gap in this case
    def generate_and_check(self, graph, configurations, decision_variables,
                           linear_energy_ranges, quadratic_energy_ranges,
                           min_classical_gap):
        """Checks that MaxGap's BQM and gap obeys the constraints set by configurations,
        linear and quadratic energy ranges, and min classical gap.

        Note: The gap is checked for whether it obeys the min classical gap constraint, and whether
        it is the largest gap for a given BQM. However, this gap may not necessarily be the largest
        gap for the given set of constraints (i.e. configurations, energy ranges), and this is not
        checked for in this function.
        """
        bqm, gap = maxgap.generate(graph, configurations, decision_variables,
                                   linear_energy_ranges,
                                   quadratic_energy_ranges,
                                   min_classical_gap)

        self.assertGreaterEqual(gap, min_classical_gap)

        # check that the bqm/graph have the same structure
        self.assertEqual(len(bqm.linear), len(graph.nodes))
        for v in bqm.linear:
            self.assertIn(v, graph.nodes)
        self.assertEqual(len(bqm.quadratic), len(graph.edges))
        for u, v in bqm.quadratic:
            self.assertIn((u, v), graph.edges)

        # now solve for the thing
        sampleset = dimod.ExactSolver().sample(bqm)

        # check that ground has 0 energy
        if len(sampleset):
            self.assertAlmostEqual(sampleset.first.energy, min(configurations.values()))

        # Get highest feasible energy
        if isinstance(configurations, dict) and configurations:
            highest_feasible_energy = max(configurations.values())
        else:
            highest_feasible_energy = 0

        # check gap and other energies
        best_gap = float('inf')
        seen = set()
        for sample, energy in sampleset.data(['sample', 'energy']):
            config = tuple(sample[v] for v in decision_variables)

            # we want the minimum energy for each config of the decision variables,
            # so once we've seen it once we can skip
            if config in seen:
                continue

            if config in configurations:
                self.assertAlmostEqual(energy, configurations[config])
                seen.add(config)
            else:
                best_gap = min(best_gap, energy - highest_feasible_energy)

        # check energy ranges
        for v, bias in bqm.linear.items():
            min_, max_ = linear_energy_ranges[v]
            self.assertGreaterEqual(bias, min_)
            self.assertLessEqual(bias, max_)

        for (u, v), bias in bqm.quadratic.items():
            min_, max_ = quadratic_energy_ranges.get((u, v), quadratic_energy_ranges.get((v, u), None))
            self.assertGreaterEqual(bias, min_)
            self.assertLessEqual(bias, max_)

        self.assertAlmostEqual(best_gap, gap)
Beispiel #6
0
    def generate_and_check(self,
                           graph,
                           configurations,
                           decision_variables,
                           linear_energy_ranges,
                           quadratic_energy_ranges,
                           min_classical_gap,
                           known_classical_gap=0):
        """Checks that MaxGap's BQM and gap obeys the constraints set by configurations,
        linear and quadratic energy ranges, and min classical gap.

        Args:
            known_classical_gap: a known gap for this graph and configuration
        """
        bqm, gap = maxgap.generate(graph, configurations, decision_variables,
                                   linear_energy_ranges,
                                   quadratic_energy_ranges, min_classical_gap)

        # Check gap
        # Note: Due to the way MaxGap searches for the maximum gap, if
        #   known_classical_gap == "maximum possible gap", then `gap` can be
        #   slightly smaller than known_classical_gap.
        self.assertGreaterEqual(gap, min_classical_gap)
        self.assertGreaterEqual(gap, known_classical_gap - MAX_GAP_DELTA)

        # check that the bqm/graph have the same structure
        self.assertEqual(len(bqm.linear), len(graph.nodes))
        for v in bqm.linear:
            self.assertIn(v, graph.nodes)
        self.assertEqual(len(bqm.quadratic), len(graph.edges))
        for u, v in bqm.quadratic:
            self.assertIn((u, v), graph.edges)

        # now solve for the thing
        sampleset = dimod.ExactSolver().sample(bqm)

        # check that ground has 0 energy
        if len(sampleset):
            self.assertAlmostEqual(sampleset.first.energy,
                                   min(configurations.values()))

        # Get highest feasible energy
        if isinstance(configurations, dict) and configurations:
            highest_feasible_energy = max(configurations.values())
        else:
            highest_feasible_energy = 0

        # check gap and other energies
        best_gap = float('inf')
        seen = set()
        for sample, energy in sampleset.data(['sample', 'energy']):
            config = tuple(sample[v] for v in decision_variables)

            # we want the minimum energy for each config of the decision variables,
            # so once we've seen it once we can skip
            if config in seen:
                continue

            if config in configurations:
                self.assertAlmostEqual(energy, configurations[config])
                seen.add(config)
            else:
                best_gap = min(best_gap, energy - highest_feasible_energy)

        # check energy ranges
        for v, bias in bqm.linear.items():
            min_, max_ = linear_energy_ranges[v]
            self.assertGreaterEqual(bias, min_)
            self.assertLessEqual(bias, max_)

        for (u, v), bias in bqm.quadratic.items():
            min_, max_ = quadratic_energy_ranges.get(
                (u, v), quadratic_energy_ranges.get((v, u), None))
            self.assertGreaterEqual(bias, min_)
            self.assertLessEqual(bias, max_)

        self.assertAlmostEqual(best_gap, gap)