QSage

Hi,

Can anyone tell me where QSage() is defined?

As dwave_sapi2 is not available I was looking around but I didn't find anything.

And if someone find "location" of QSage() if he can write a small example on usage.

Thank u very much!

0

Comments

23 comments
  • Hi Alberto,

    Unfortunately the QSage feature is currently only available in our legacy Sapi client library, which is available to users who have custom contracts with D-Wave.


     

    0
    Comment actions Permalink
  • Hi David,

    Thank you for reply. What a pity. Is there an alternative to QSage?

    0
    Comment actions Permalink
  • Hi Alberto,

    Unfortunately we do not have an alternative to QSage that takes an arbitrary objective function.

    Hopefully something will make its way to Ocean at some point.

    If you are looking to solve problems larger than the capacity of the QPU, you can take a look at dwave-hybrid:
    https://docs.ocean.dwavesys.com/projects/hybrid/en/latest/

    Although it is still in the early development stages, it offers some powerful improvements to the similar qbsolv, which does not allow for parallel computation of problem sub-sets:
    https://docs.ocean.dwavesys.com/projects/qbsolv/en/latest/

    Hopefully these tools are useful for your research purposes.
    If you could tell us a little bit more about your use case, maybe we can help guide you towards some better information.

    0
    Comment actions Permalink
  • Great, Thank you.

    It's for my thesis project. I'm trying to solve some instances of Quadratic Assignment Problem but since a constrained QAP problem of size n can be transformed to an unconstrained QUBO problem of size n^2 by introducing a penalty function that enforces 1-to-1 assignments this transformation expands the problem size. So, the instances number that I can solve on D-Wave 200Q is limited.That's why I thought that QSage could help me. 

    I'm wrong?

    0
    Comment actions Permalink
  • In this case, you could use dwave-hybrid. 

    0
    Comment actions Permalink
  • The dwave-hybrid library lets you take a large QUBO and solve it using a number of methods, including combining classical and quantum computing power.

    It is still in development, but it offers some advantages over its predecessors, with asynchronous resolution of decomposed problem parts.

    This allows for more flexible workflow design.

    Here is a link to the latest information on dwave-hybrid:
    https://docs.ocean.dwavesys.com/projects/hybrid/en/latest/

    The qbsolv library has similar functionality, but does not offer asynchronous problem solving.

    As you already have a QUBO formulated problem, going with asynchronous decomposition solvers that are available in the dwave-hybrid library is a reasonable option. 

    It is not necessary to use QSage in this case.

    0
    Comment actions Permalink
  • Hi David,

    I've a question. Is there a way to get qpu_time only within QBsolv or D-Wave-Hybrid response? In alternative how to compute time to solve, because within D-Wave response there is a field info with timing info but this field,within QBsolv or Hybrid, is empty.

    Thank u.

    0
    Comment actions Permalink
  • Hi Alberto,

    There is a way to get the timing info for dwave-hybrid.

    Here is an example (the only line of concern is the last one that is in bold):

    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

    # Construct a problem
    bqm = dimod.BinaryQuadraticModel({}, {'ab': 1, 'bc': -1, 'ca': 1}, 0, dimod.SPIN)

    # Define the solver
    iteration = RacingBranches(
    InterruptableTabuSampler(),
    EnergyImpactDecomposer(size=2)
    | QPUSubproblemAutoEmbeddingSampler()
    | SplatComposer()
    ) | ArgMin()
    main = Loop(iteration, max_iter=10, convergence=3)

    # Solve the problem
    init_state = State.from_sample(min_sample(bqm), bqm)
    solution = main.run(init_state).result()

    # Print results
    print("Solution: sample={s.samples.first}".format(s=solution))

    main.timers

    The base code is from the documentation, and at the end I have included references to the timers.
    Here is a link to the base code:
    https://docs.ocean.dwavesys.com/projects/hybrid/en/latest/

    0
    Comment actions Permalink
  • Hi David,

    Thank you for reply. I saw that the timer variable contains various dispatch. I searched their meaning but I didn't find anything. Can you explain me please? 

    Thank u again.

    0
    Comment actions Permalink
  • Hi Alberto,

    At present, this feature is a work in progress and is not yet documented.

    The most important value in the timer dictionary is the dispatch.next value.

    In this case it corresponds to one run through of main. 
    It corresponds to the steps taken to get the next state.

    The other values in timer are probably not as much of interest, but could also be useful in some cases.

    The dispatch value corresponds to the total time for that runnable.
    The dispatch.resolve value corresponds to how long a runnable waits on a previous run to finish running.
    The dispatch.init value corresponds to a one time setup operation.

    You will notice that multiple runs of the runnable will result in multiple time values appearing in the timer.
    The runnables are stateful and keep information for individual runs.
    The init value should only happen once.

    There are a couple of useful functions you can run to look at the structure of the pipeline and the counter/timer information.

    In this case we ran both functions on "main" but you can use it on other parts such as "iteration" or other runnables.

    This function will print out the hierarchy of the given runnable:
    hybrid.print_structure(main)

    This function will recursively print out all of the counter/timer information of the runnable:
    hybrid.print_counters(main)

    You can see that this recursive print shows the count, cumulative time, and average time for each run.

    I hope this is informative enough for you to understand what kind of information is in the timer dictionary.

    Feel free to ask for clarification or for more information.

    0
    Comment actions Permalink
  • Oh very very helpful, thank u David.

    I've another question (sorry): is there a way, in RacingBranches, to get the sampler that return the solution? That is, when I get the solution from main i don't know if this is returned from tabu sampler or from qpu sampler. 

    I don't know if I explained my "problem" well.

    1
    Comment actions Permalink
  • You can see what solver is being used by using the hybrid.print_counters() function.

    This function gives the counts of how many times a given solver is used.

    RacingBranches might result in one solver being used, followed by the other, so you can see exactly how many times each was used by looking at the count functions.

    Here is output from when I ran the sample code:

    hybrid.print_counters(main)
    * Loop
    (timers)
    - 'dispatch': cnt = 1, cumtime = 0.003 s, avgtime = 0.003 s
    - 'dispatch.init': cnt = 1, cumtime = 0.000 s, avgtime = 0.000 s
    - 'dispatch.next': cnt = 1, cumtime = 3.577 s, avgtime = 3.577 s
    - 'dispatch.resolve': cnt = 1, cumtime = 0.000 s, avgtime = 0.000 s
    * Branch
    (timers)
    - 'dispatch': cnt = 4, cumtime = 3.575 s, avgtime = 0.894 s
    - 'dispatch.init': cnt = 1, cumtime = 0.000 s, avgtime = 0.000 s
    - 'dispatch.next': cnt = 4, cumtime = 3.575 s, avgtime = 0.894 s
    - 'dispatch.resolve': cnt = 4, cumtime = 0.000 s, avgtime = 0.000 s
    * RacingBranches
    (timers)
    - 'dispatch': cnt = 4, cumtime = 3.572 s, avgtime = 0.893 s
    - 'dispatch.init': cnt = 1, cumtime = 0.000 s, avgtime = 0.000 s
    - 'dispatch.next': cnt = 4, cumtime = 3.572 s, avgtime = 0.893 s
    - 'dispatch.resolve': cnt = 4, cumtime = 0.000 s, avgtime = 0.000 s
    * InterruptableTabuSampler
    (timers)
    - 'dispatch': cnt = 4, cumtime = 0.123 s, avgtime = 0.031 s
    - 'dispatch.init': cnt = 1, cumtime = 0.000 s, avgtime = 0.000 s
    - 'dispatch.next': cnt = 4, cumtime = 3.569 s, avgtime = 0.892 s
    - 'dispatch.resolve': cnt = 4, cumtime = 0.000 s, avgtime = 0.000 s
    * Branch
    (timers)
    - 'dispatch': cnt = 4, cumtime = 0.139 s, avgtime = 0.035 s
    - 'dispatch.init': cnt = 1, cumtime = 0.000 s, avgtime = 0.000 s
    - 'dispatch.next': cnt = 4, cumtime = 3.360 s, avgtime = 0.840 s
    - 'dispatch.resolve': cnt = 4, cumtime = 0.000 s, avgtime = 0.000 s
    * EnergyImpactDecomposer
    (timers)
    - 'dispatch': cnt = 4, cumtime = 0.005 s, avgtime = 0.001 s
    - 'dispatch.init': cnt = 1, cumtime = 0.000 s, avgtime = 0.000 s
    - 'dispatch.next': cnt = 4, cumtime = 0.005 s, avgtime = 0.001 s
    - 'dispatch.resolve': cnt = 4, cumtime = 0.000 s, avgtime = 0.000 s
    * QPUSubproblemAutoEmbeddingSampler
    (timers)
    - 'dispatch': cnt = 4, cumtime = 3.349 s, avgtime = 0.837 s
    - 'dispatch.init': cnt = 1, cumtime = 0.000 s, avgtime = 0.000 s
    - 'dispatch.next': cnt = 4, cumtime = 3.349 s, avgtime = 0.837 s
    - 'dispatch.resolve': cnt = 4, cumtime = 0.000 s, avgtime = 0.000 s
    * SplatComposer
    (timers)
    - 'dispatch': cnt = 4, cumtime = 0.006 s, avgtime = 0.001 s
    - 'dispatch.init': cnt = 1, cumtime = 0.000 s, avgtime = 0.000 s
    - 'dispatch.next': cnt = 4, cumtime = 0.006 s, avgtime = 0.001 s
    - 'dispatch.resolve': cnt = 4, cumtime = 0.000 s, avgtime = 0.000 s
    * ArgMin
    (timers)
    - 'dispatch': cnt = 4, cumtime = 0.002 s, avgtime = 0.001 s
    - 'dispatch.init': cnt = 1, cumtime = 0.000 s, avgtime = 0.000 s
    - 'dispatch.next': cnt = 4, cumtime = 0.002 s, avgtime = 0.001 s
    - 'dispatch.resolve': cnt = 4, cumtime = 0.000 s, avgtime = 0.000 s
    (counters)
    - 'branch-0': 3
    - 'branch-1': 1

    The cnt value corresponds to the count value. You can see for instance that the QPU was run 4 times from these lines:

    * QPUSubproblemAutoEmbeddingSampler
    (timers)
    - 'dispatch': cnt = 4, cumtime = 3.349 s, avgtime = 0.837 s

    If you want to have more fine-grained control over logging information, or timer information, it is possible to add logging statements directly into QPUSubproblemAutoEmbeddingSampler.

    It is also possible to write custom subproblem samplers to add desired functionality into each iteration of the loop.

    0
    Comment actions Permalink
  • Thank you David, really.

    Have you some advice or tiny example to develop custom subproblem samplers?

    0
    Comment actions Permalink
  • Hi Alberto,

    No problem!

    As for the example, I am working on one now, so I will link to it once it is ready.

    0
    Comment actions Permalink
  • Here is a basic example showing how to write a custom runnable, how to add timing information to the timers dictionary, and how to access the timing information after the code has run:

    from dwave.system.samplers import DWaveSampler
    from dwave.system.composites import EmbeddingComposite
    from hybrid.core import Runnable
    from hybrid import traits

    class CustomQPUSubproblemAutoEmbeddingSampler(Runnable, traits.SubproblemSampler):
    """A quantum sampler for a subproblem with automated heuristic minor-embedding.

    Args:
    num_reads (int, optional, default=100):
    Number of states (output solutions) to read from the sampler.
    qpu_sampler (:class:`dimod.Sampler`, optional, default=EmbeddingComposite(DWaveSampler())):
    Quantum sampler such as a D-Wave system. If sampler is structured,
    it will be converted to unstructured via :class:`~dwave.system.composited.EmbeddingComposite`.

    Examples:
    See examples on https://docs.ocean.dwavesys.com/projects/hybrid/en/latest/reference/samplers.html#examples.
    """

    def __init__(self, num_reads=100, qpu_sampler=None):
    super(CustomQPUSubproblemAutoEmbeddingSampler, self).__init__()

    self.num_reads = num_reads

    if qpu_sampler is None:
    qpu_sampler = DWaveSampler()

    # convert the structured sampler to unstructured
    if isinstance(qpu_sampler, dimod.Structured):
    self.sampler = EmbeddingComposite(qpu_sampler)
    else:
    self.sampler = qpu_sampler

    def __repr__(self):
    return ("{self}(num_reads={self.num_reads!r}, "
    "qpu_sampler={self.sampler!r})").format(self=self)

    def next(self, state):
    response = self.sampler.sample(state.subproblem, num_reads=self.num_reads)
    self.timers.setdefault('qpu_timing', []).append(response.info["timing"].copy())
    return state.updated(subsamples=response)



    import dimod
    from hybrid.samplers import 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

    # Construct a problem
    bqm = dimod.BinaryQuadraticModel({}, {'ab': 1, 'bc': -1, 'ca': 1}, 0, dimod.SPIN)

    custom = CustomQPUSubproblemAutoEmbeddingSampler()

    # Define the solver
    iteration = RacingBranches(
    InterruptableTabuSampler(),
    EnergyImpactDecomposer(size=2)
    | custom
    | SplatComposer()
    ) | ArgMin()
    main = Loop(iteration, max_iter=10, convergence=3)

    # Solve the problem
    init_state = State.from_sample(min_sample(bqm), bqm)
    solution = main.run(init_state).result()

    # Print results
    print("Solution: sample={s.samples.first}".format(s=solution))

    print(custom.timers['qpu_timing'])

    I pulled the base code for the top section from samplers.py in hybrid:
    In the docs here:
    https://docs.ocean.dwavesys.com/projects/hybrid/en/latest/
    Or in GitHub here:
    https://github.com/dwavesystems/dwave-hybrid

    I pulled the base code for the bottom section from the hybrid sample code for QPUSubproblemAutoEmbeddingSampler:
    https://github.com/dwavesystems/dwave-hybrid/blob/master/hybrid/samplers.py

    Here is some additional documentation on the other samplers and what they do:
    https://docs.ocean.dwavesys.com/projects/hybrid/en/latest/reference/samplers.html

    I have highlighted key differences in bold.

    You can see that putting a runnable in its own variable allows the timers dictionary to be accessed independently later.

    It is also possible to call hybrid.print_counters() on just the custom variable.

    1
    Comment actions Permalink
  • Hi,

    Thank you for sharing, I thought it was more difficult.

    I've a question (theory): the procedure to generate subproblem according to size parameter is like QBsolv procedure? And how the response/solution is related to this parameter? 

    0
    Comment actions Permalink
  • QBSolv is another library altogether that has similar functionality.

    To break a problem down into smaller pieces with dwave-hybrid, you can use decomposers.

    Here is a link to the dwave-hybrid decomposers with links to examples:
    https://docs.ocean.dwavesys.com/projects/hybrid/en/latest/reference/decomposers.html

    The response is the response to the problem set to the QPU in the above example, just like if you sent a problem to the QPU directly.

    Essentially using the runnable is just sending problems to the QPU and other solvers repeatedly, looking for the best solution.

    This can also be done by breaking the problem up with decomposers and sending the problem to various solvers in parts.

    0
    Comment actions Permalink
  • Yes I know, but I meant the procedure inside the decomposer. I ask this because I would like to define it better on a theoretical level in my thesis.

    0
    Comment actions Permalink
  • Hi David,

    Is it possibile that sometimes D-Wave Hybrid return no embedding found?

    Thank u.

    0
    Comment actions Permalink
  • Hi Alberto,

    It is possible to get a no embedding found error, even in some scenarios where an embedding was previously found.
    This can happen more frequently when the embedding gets close to the maximum embeddable size on the qpu.

    As for the decomposers, this is an area that we are actively working on.
    There are a few decomposers that use various approaches to breaking down the problem.

    The most basic one measures the energy impact of each qubit by flipping its value and seeing what effect it has on the energy of the overall system.

    Here's the link again for reference:
    https://docs.ocean.dwavesys.com/projects/hybrid/en/latest/reference/decomposers.html

    This is an area that is being expanded upon.
    Hopefully we will have more tutorials and documentation in the near future.
    In addition, users can write custom decomposers as well.

    Breaking the problem apart is a very difficult challenge.
    It is unique to each problem.
    One decomposer can be highly effective for one problem and highly ineffective for another.

    You can see the code for all of the decomposers, as there are links in the documentation if you click on "source".
    This way you can take a look at the process the decomposer uses to break apart the problem.
    Here is a link to the EnergyImpactDecomposer:
    https://docs.ocean.dwavesys.com/projects/hybrid/en/latest/_modules/hybrid/decomposers.html#EnergyImpactDecomposer

    Please let us know if you have any more questions.

    0
    Comment actions Permalink
  • Hi Alberto,

    There is also the KerberosSampler.

    Here is some documentation:
    https://github.com/dwavesystems/dwave-hybrid/blob/master/hybrid/reference/kerberos.py

    Here is a code example taken from the documentation:
    >>> import dimod
    >>> response = KerberosSampler().sample_ising(
    ... {'a': -0.5, 'b': 1.0}, {('a', 'b'): -1})
    >>> response.data_vectors['energy']
    array([-1.5, -1.5, -1.5, -1.5, -1.5, -1.5, -1.5, -1.5, -1.5, -1.5])

    This solver is useful because you can pass it the problem without worrying about the size and it will solve the problem and return a result.
    It is still in the development stages, so please stay tuned for updates.

    0
    Comment actions Permalink
  • Hi,

     I'm facinig a problem similar to what Alberto C is working on. I'm having a hard time to understand whether the solution is found by the Tabu solver (i.e. by the CPU!) or by the decomposed problem run on the QPU. The output of the timing function is quite clear, but I'm not sure it really helps on that front. Just for testing purposes, I removed the InterruptableTabuSampler from the list of racing branches, but the solution I get is all zeros, which is very suspicious!

     

                iteration = hybrid.RacingBranches(

                    hybrid.Identity(),

    #                hybrid.InterruptableTabuSampler(),

                    hybrid.EnergyImpactDecomposer(size=2)

                    | hybrid.QPUSubproblemAutoEmbeddingSampler(num_reads=100)

                    | hybrid.SplatComposer()

                ) | hybrid.ArgMin()

                workflow = hybrid.LoopUntilNoImprovement(iteration, convergence=3)
    0
    Comment actions Permalink
  • Hi,

    Sorry to hear you are having problems!

    The returned values will depend on what your input problem looks like, as much as the workflow you use.

    Are the energies that you are getting back with the TabuSampler lower than the all 0 result energy?

    Is it possible that the all 0 state is the lowest or near lowest energy state?

    Try running this Qubo:

    Q = {(0, 1): 4, (0, 2): 4, (1, 2): 4, (0, 0): -2, (1, 1): -2, (2, 2): -2}

    I get a mixed return of 1s and 0s.

    Have you also tried removing the QPU and seeing what results you get back?

    I hope this was helpful.

    0
    Comment actions Permalink

Please sign in to leave a comment.

Didn't find what you were looking for?

New post