Add lattice-utils to dwave-experimental#44
Conversation
-move initialization of num_spins to subclass
thisac
left a comment
There was a problem hiding this comment.
Thanks @SebastianGitt! First pass of the lattice submodule.
- I'd recommend running a formatter (black) since all of these files are new and there are a few places with incorrect spacing, etc.
- I think the saving/loading of embeddings can be refined a bit, but I'll have a closer look at it later.
-added more comprehensive docstrings -fixed formatting issues
-added more comprehensive docstrings -fixed formatting issues
…dwave-experimental into add-lattice-utils
-removed spin reversal transform functionality -uses automorphism module already in dwave-experimental -uses data classes for experiment configs
-removed spin reversal transform functionality -uses automorphism module already in dwave-experimental -uses data classes for experiment configs -formatted using black
andrew-d-king
left a comment
There was a problem hiding this comment.
Looks generally good; I am not able to pore over it in detail but it all seems sensible.
|
|
||
| return sampler_call | ||
|
|
||
| def _format_parameter_list( |
There was a problem hiding this comment.
Maybe not for this PR, but ultimately I think that a parameter class should be written, with a format parameter (basically sig figs, for float parameters). It's a bit ugly to do it ad hoc, as I've written it.
thisac
left a comment
There was a problem hiding this comment.
Still have experiment module to look at. Re. the tests, it probably makes sense splitting them up into several files collected in a test_lattice_utils folder.
-added progress bars using TQDM -added docstrings to the evaluation methods in observable.py -misc cleanup
-added progress bars using TQDM -added docstrings to the evaluation methods in observable.py -misc cleanup
…dwave-experimental into add-lattice-utils
jackraymond
left a comment
There was a problem hiding this comment.
Nice job, especially the integration of new automorphism functionality to make the shimming tutorial generalizable.
Some of this code is not lattice specific, I'd prefer it weren't bundled up in a lattice utility. Like embed_bqm (which mirrors a dwave.system) and unembed_bqm (which perhaps is, or should be dwave.system method), bootstrapping, bitstring compression. Ideally we could think how to extract it for more general use.
Examples don't run well, or at all for me. Adding an interface and some additional help messages on what is going on would be great. Perhaps cite papers etc where statistics are used.
There are a lot of magic numbers/methods floating around, if these could be promoted to function arguments and justified that would be great.
Some other naming conventions I found a bit confusing.
-fix failing tests due to updated method signatures with explicit arguments -rename energy_scale to signed_energy_scale
-remove existing examples and replaced with a new 1D chain shimming example -restructure tests into separate files in a new folder -update optimize function to accept custom dimod solvers -expand docstring for ``embed_bqm`` method to explain difference from existing methods in dwave-system -promote ``optimize`` and ``make_networkx_graph`` to public methods
thisac
left a comment
There was a problem hiding this comment.
Looking good so far! A couple of comments and improvements before ready to merge.
One thing that we discussed already: tqdm should be made into an optional dependency. Currently it's required to install.
|
|
||
| import dimod | ||
| import numpy as np | ||
| from tqdm.auto import tqdm |
There was a problem hiding this comment.
Didn't we say that we'd make tqdm optional?
| if not quiet: | ||
| print(f"Loaded {filename} at {datetime.now()}") |
There was a problem hiding this comment.
For now let's remove print statements from the package. We can think about either adding in logging messages if we really think it's needed, or warnings where reasonable.
| for param_name, param_val in param.items(): | ||
| self.param[param_name] = param_val |
There was a problem hiding this comment.
Any reason for not doing
| for param_name, param_val in param.items(): | |
| self.param[param_name] = param_val | |
| self.param.update(param) |
| self.sampler = sampler | ||
| self.reference_energy_sampler = reference_energy_sampler | ||
| self.reference_energy_sampler_kwargs = reference_energy_sampler_kwargs | ||
| self.param = dict(vars(config)) |
There was a problem hiding this comment.
vars returns a dict already
| self.param = dict(vars(config)) | |
| self.param = vars(config) |
| if ignore_shim: | ||
| result_fields.remove("shimdata") |
There was a problem hiding this comment.
Based on docs it sounds like this should maybe be outside the if result_fields is None?
| A numpy array containing the pairwise spin correlations for each coupler. | ||
| """ | ||
| sample_array = dimod.as_samples(sample_set)[0].astype(float) | ||
| if len(experiment.inst.edge_list) == 0: |
There was a problem hiding this comment.
Should work and would in that case be recommended.
| if len(experiment.inst.edge_list) == 0: | |
| if not experiment.inst.edge_list: |
| A numpy array containing the mean coupler frustration for each edge. | ||
| """ | ||
| sample_array = dimod.as_samples(sample_set)[0].astype(float) | ||
| if len(experiment.inst.edge_list) == 0: |
There was a problem hiding this comment.
| if len(experiment.inst.edge_list) == 0: | |
| if not experiment.inst.edge_list: |
|
|
||
| return energy | ||
|
|
||
| def load( |
There was a problem hiding this comment.
A few of the methods below are missing complete docstrings (load, save, and update).
| if new_energy < reference_energy: | ||
| if path is None: | ||
| path = get_reference_energy_path(bqm, experiment) | ||
| self.save(path, new_energy, sample, reference_method_string) | ||
| else: | ||
| raise ValueError("New energy is not better than reference energy, not updating.") |
There was a problem hiding this comment.
Minor, but I'd turn this around
| if new_energy < reference_energy: | |
| if path is None: | |
| path = get_reference_energy_path(bqm, experiment) | |
| self.save(path, new_energy, sample, reference_method_string) | |
| else: | |
| raise ValueError("New energy is not better than reference energy, not updating.") | |
| if new_energy >= reference_energy: | |
| raise ValueError("New energy is not better than reference energy, not updating.") | |
| if path is None: | |
| path = get_reference_energy_path(bqm, experiment) | |
| self.save(path, new_energy, sample, reference_method_string) |
| features: | ||
| - | | ||
| Add ``dwave.experimental.lattice_utils`` submodule with utilities for | ||
| constructing lattice graphs (``Chain``, ``Triangular``, | ||
| ``DimerizedTriangular``, ``EmbeddedLattice``), evaluating physics | ||
| observables on sample sets, and running shimmed Ising experiments on | ||
| QPU samplers. |
There was a problem hiding this comment.
This should probably be split up into several features.
| if self.param.get("fast_anneal", False): | ||
| ret["fast_anneal"] = True | ||
|
|
||
| ret["annealing_time"] = self.param["anneal_time"] |
There was a problem hiding this comment.
Since elsewhere it seems like either "anneal_schedule" or "anneal_time" can be used, is this also the case here? If not, perhaps add a comment on why.
if "anneal_schedule" in self.param:
...
elif "anneal_time" in self.param:
ret["annealing_time"] = self.param["anneal_time"]
| for i in range(n - 1): | ||
| yield (i, i + 1) | ||
|
|
||
| if self.periodic[0] and n > 1: |
There was a problem hiding this comment.
Should this be n > 2? As it currently stands, when self.dimensions[0] == 2 this would yield both (0, 1) and (1, 0).
| if self.periodic[0] and n > 1: | |
| if self.periodic[0] and n > 2: |
| if qubit_orbits is not None and coupler_orbits is not None: | ||
| if len(qubit_orbits) != self.num_spins: | ||
| raise ValueError( | ||
| f"qubit_orbits must have length {self.num_spins}, got {len(qubit_orbits)}." | ||
| ) | ||
| if len(coupler_orbits) != self.num_edges: | ||
| raise ValueError( | ||
| f"coupler_orbits must have length {self.num_edges}, " | ||
| f"got {len(coupler_orbits)}." | ||
| ) |
There was a problem hiding this comment.
If either qubit_orbits or coupler_orbits is None, then the other won't be checked (since this states and).
Should these checks be separated into:
if qubit_orbits and len(qubit_orbits) != self.num_spins:
raise ValueError(
f"qubit_orbits must have length {self.num_spins}, got {len(qubit_orbits)}."
)and
if coupler_orbits and len(coupler_orbits) != self.num_edges:
raise ValueError(
f"coupler_orbits must have length {self.num_edges}, "
f"got {len(coupler_orbits)}."
)| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
|
|
||
| """Shimming example for 1D Ising chain.""" |
There was a problem hiding this comment.
Let's also rename this file to just 1D_Ising_chain_shim.py or maybe prefix it with lattice_utils or even put it in a lattice_utils subfolder since there are quite a few more lattice-utils examples incoming, no?
|
|
||
| from dwave.system import DWaveSampler | ||
| import matplotlib.pyplot as plt | ||
| from matplotlib.colors import to_rgb |
There was a problem hiding this comment.
| from matplotlib.colors import to_rgb |
| from dwave.experimental.lattice_utils import lattice, experiment, observable | ||
| from dwave.experimental.lattice_utils.utils import bootstrap, confidence_interval |
There was a problem hiding this comment.
Not used?
| from dwave.experimental.lattice_utils import lattice, experiment, observable | |
| from dwave.experimental.lattice_utils.utils import bootstrap, confidence_interval | |
| from dwave.experimental.lattice_utils import lattice, experiment |
| self.observables_to_collect = { | ||
| QubitMagnetization(), | ||
| CouplerCorrelation(), | ||
| CouplerFrustration(), | ||
| SampleEnergy(), | ||
| BitpackedSpins(), | ||
| ReferenceEnergy(), | ||
| } |
There was a problem hiding this comment.
Actually, does it even make sense having this be a set? Wouldn't a tuple be a more reasonable type? That nulls my other comment about not having to cast this to a set below (although I think not using sets at all here might make more sense).
Add a minimal version of LatQA to dwave-experimental as a submodule named
lattice-utils. Currently supports examples of a 1D Ising Chain and a 2D dimerized triangular lattice. Tests still need to be added to the pull request.