Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

penaltymodel-maxgap energy levels #76

Merged
merged 6 commits into from
Jan 17, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 35 additions & 20 deletions penaltymodel_maxgap/penaltymodel/maxgap/generation.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
# Copyright 2019 D-Wave Systems Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# ================================================================================================
"""
.. [DO] Bian et al., "Discrete optimization using quantum annealing on sparse Ising models",
https://www.frontiersin.org/articles/10.3389/fphy.2014.00056/full
Expand All @@ -9,18 +24,19 @@

import itertools

from six import iteritems
import penaltymodel.core as pm
import dimod

from pysmt.shortcuts import Solver

from penaltymodel.core import ImpossiblePenaltyModel
from penaltymodel.maxgap.smt import Table

__all__ = ['generate_ising']
__all__ = 'generate',


def generate_ising(graph, feasible_configurations, decision_variables,
linear_energy_ranges, quadratic_energy_ranges, min_classical_gap,
smt_solver_name):
def generate(graph, feasible_configurations, decision_variables,
linear_energy_ranges, quadratic_energy_ranges, min_classical_gap,
smt_solver_name=None):
"""Generates the Ising model that induces the given feasible configurations.

Args:
Expand All @@ -31,7 +47,7 @@ def generate_ising(graph, feasible_configurations, decision_variables,
decision_variables (list/tuple): Which variables in the graph are
assigned as decision variables.
linear_energy_ranges (dict, optional): A dict of the form
{v: (min, max, ...} where min and max are the range
{v: (min, max), ...} where min and max are the range
of values allowed to v.
quadratic_energy_ranges (dict): A dict of the form
{(u, v): (min, max), ...} where min and max are the range
Expand All @@ -44,11 +60,7 @@ def generate_ising(graph, feasible_configurations, decision_variables,
Returns:
tuple: A 4-tuple containing:

dict: The linear biases of the Ising problem.

dict: The quadratic biases of the Ising problem.

float: The ground energy of the Ising problem.
:obj:`dimod.BinaryQuadraticModel`

float: The classical energy gap between ground and the first
excited state.
Expand All @@ -58,6 +70,9 @@ def generate_ising(graph, feasible_configurations, decision_variables,
to a non-zero infeasible gap.

"""
if len(graph) == 0:
return dimod.BinaryQuadraticModel.empty(dimod.SPIN), float('inf')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When MIP finds that it is given a table with no feasible configurations, it returns a zero-ed out BQM and a gap of zero. Here, MaxGap is returning an empty model and a gap of infinity. Perhaps we should have a consistent default gap when bad argument is given?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea. What do you think makes more sense?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like a gap of infinity because I think it's unlikely that a user would choose that as a gap. Hence, when you see it's an infinity you know it's an error or a computer generated default.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is out-of-scope for this PR then. See #79


# we need to build a Table. The table encodes all of the information used by the smt solver
table = Table(graph, decision_variables, linear_energy_ranges, quadratic_energy_ranges)

Expand Down Expand Up @@ -118,16 +133,16 @@ def generate_ising(graph, feasible_configurations, decision_variables,
g = min(gmin + .1, (gmax + gmin) / 2)

else:
raise pm.ImpossiblePenaltyModel("Model cannot be built")
raise ImpossiblePenaltyModel("Model cannot be built")

# finally we need to convert our values back into python floats.

theta = table.theta
linear = {v: float(model.get_py_value(bias))
for v, bias in iteritems(theta.linear)}
quadratic = {(u, v): float(model.get_py_value(bias))
for (u, v), bias in iteritems(theta.quadratic)}
ground_energy = -float(model.get_py_value(theta.offset))
classical_gap = float(model.get_py_value(table.gap))

return linear, quadratic, ground_energy, classical_gap
# if the problem is fully specified (or empty) it has infinite gap
if (len(decision_variables) == len(graph) and
decision_variables and # at least one variable
len(feasible_configurations) == 2*len(decision_variables)):
classical_gap = float('inf')

return table.theta.to_bqm(model), classical_gap
44 changes: 30 additions & 14 deletions penaltymodel_maxgap/penaltymodel/maxgap/interface.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,24 @@
from six import iteritems
# Copyright 2019 D-Wave Systems Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# ================================================================================================
import penaltymodel.core as pm
import dimod

from penaltymodel.maxgap.generation import generate_ising
from penaltymodel.maxgap.generation import generate

__all__ = ['get_penalty_model']
__all__ = 'get_penalty_model',


@pm.penaltymodel_factory(-100) # set the priority to low
Expand All @@ -30,21 +44,23 @@ def get_penalty_model(specification):
feasible_configurations = specification.feasible_configurations
if specification.vartype is dimod.BINARY:
feasible_configurations = {tuple(2 * v - 1 for v in config): en
for config, en in iteritems(feasible_configurations)}
for config, en in feasible_configurations.items()}

# convert ising_quadratic_ranges to the form we expect
ising_quadratic_ranges = specification.ising_quadratic_ranges
quadratic_ranges = {(u, v): ising_quadratic_ranges[u][v] for u, v in specification.graph.edges}

linear, quadratic, ground, gap = generate_ising(specification.graph,
feasible_configurations,
specification.decision_variables,
specification.ising_linear_ranges,
quadratic_ranges,
specification.min_classical_gap,
None) # unspecified smt solver
bqm, gap = generate(specification.graph,
feasible_configurations,
specification.decision_variables,
specification.ising_linear_ranges,
quadratic_ranges,
specification.min_classical_gap,
None) # unspecified smt solver

# set of the model with 0.0 offset
model = dimod.BinaryQuadraticModel(linear, quadratic, 0.0, dimod.SPIN)
try:
ground = min(feasible_configurations.values())
except ValueError:
ground = 0.0 # if empty

return pm.PenaltyModel.from_specification(specification, model, gap, ground)
return pm.PenaltyModel.from_specification(specification, bqm, gap, ground)
15 changes: 15 additions & 0 deletions penaltymodel_maxgap/penaltymodel/maxgap/package_info.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
# Copyright 2019 D-Wave Systems Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# ================================================================================================
__version__ = '0.5.0'
__author__ = 'D-Wave Systems Inc.'
__authoremail__ = '[email protected]'
Expand Down
Loading