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

Weighted trajectories #2369

Merged
merged 31 commits into from May 7, 2024
Merged

Weighted trajectories #2369

merged 31 commits into from May 7, 2024

Conversation

pmenczel
Copy link
Member

@pmenczel pmenczel commented Mar 29, 2024

Description

Introduces weights for the trajectories of trajectory solvers. These weights enter all trajectory averages. This concept is a generalization of two things that are already in qutip:

  • The influence martingale in nm_mcsolve is simply a time-dependent weight from this point of view
  • The "improved sampling" option for mcsolve weights the no-jump trajectory with its exact probability, and then only samples from the ensemble of trajectories with at least one jump.

Currently, these two things cannot be combined: "improved sampling" does not work for nm_mcsolve. With these changes, it would work.
In the future, there might be at least one more application for weighted trajectories:

  • For mixed initial states in mcsolve, it might be useful to allow sampling of initial states with frequencies or probabilities that are different from their true prefactor in the initial state. The ratio between the sampling frequencies and true prefactors would be weights on the trajectories.

This also fixes some things in merging McResults and NmmcResults, and perhaps fixes the target tolerance computation for the "improved sampling" option.

Implementation

A difficulty in the implementation is that the weights depend on the total number of trajectories in different ways. For example, the no-jump trajectory has a fixed weight that never changes, but all other trajectories come with prefactors $(1-p_0) / (N-1)$ where $p_0$ is the probability of the no-jump trajectory, and $N$ the total number of trajectories (including the one no-jump trajectory). For this reason, I separate weights into absolute weights (like $p_0$) and relative weights (like the others). The average is performed as follows:

$$ \bar\rho = \sum_{T: \text{abs}} w_a(T) w_r(T) \rho(T) + \frac{1}{N_{\text{rel}}} \sum _{T: \text{rel}} w_r(T) \rho(T) $$

where the first sum is over all trajectories T that have absolute weights, and the second sum over all other trajectories. Here, $w_a(T)$ and $w_r(T)$ are the absolute and relative weights, $\rho(T)$ the state associated with a trajectory, and $N_{\text{rel}}$ the number of trajectories that have not been assigned absolute weights.

Intuitively, the idea is that absolute weights are for trajectories where the contribution to the full state is known exactly, and the trajectories without absolute weights are the stochastic part.

Things to do

  • Update tests and create new ones
  • Update photocurrent to work with weights
  • Update target tolerance calculation to work with weights
  • Think about how result objects should be added if both contain trajectories with absolute weights
  • Double-check that this creates no problems for stochastic solvers
  • Towncrier entry

Related issues or PRs
#2235

@coveralls
Copy link

coveralls commented Mar 29, 2024

Coverage Status

coverage: 86.098% (+0.008%) from 86.09%
when pulling cd96a59 on pmenczel:weighted-trajectories
into 320996b on qutip:master.

Copy link
Member

@Ericgig Ericgig left a comment

Choose a reason for hiding this comment

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

Thank you for merging the results classes.

The default seems to be a relative weight of 1. Meaning that it should work fine in solver which does not use it (stochastic). But should they just keep using Result until we have a use for those weight?

It there a balancing tools for weight values?
If there are 2 runs, can both have a relative weight of 10 or 0.1 without affecting the end results? If there are 2 starting kets that we want to merge with a weight of 10:1, does the actual values matter or only the ratio?

I feel it would be safer to compute the relative weight as W(T) / sum_T(W(T)) instead of using the number of trajectories.

I am not sure how it works with weight changing over time...

Comment on lines 1204 to 1205
# TODO this is wrong if trajectories have weights
# unclear how to implement in case of time-dependent weights
Copy link
Member

Choose a reason for hiding this comment

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

Does photocurrent make sense for nm_mcsolve?
If it does not, we could raise an error when used with time-dependent weights.

Copy link
Member Author

Choose a reason for hiding this comment

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

It probably does not make sense for nm_mcsolve. I have added an error in the case of time-dependent weights, and removed the tests for it.

qutip/solver/result.py Outdated Show resolved Hide resolved
qutip/solver/result.py Outdated Show resolved Hide resolved
@pmenczel
Copy link
Member Author

pmenczel commented Apr 1, 2024

Thank you for your review.

The default seems to be a relative weight of 1. Meaning that it should work fine in solver which does not use it (stochastic). But should they just keep using Result until we have a use for those weight?

I let them use Result again for now. I needed to add some lines here so that the new MultiTrajResult can deal with it. (Which might be a good idea anyway.)

It there a balancing tools for weight values? If there are 2 runs, can both have a relative weight of 10 or 0.1 without affecting the end results? If there are 2 starting kets that we want to merge with a weight of 10:1, does the actual values matter or only the ratio?

I have added a merge function that lets the user merge trajectory results with custom relative weights. I think trajectories with absolute or relative weights are handled consistently. Calling result1 + result2 uses default values for the relative weights, chosen such that every trajectory contributes equally.
(That doesn't currently work for the stochastic solvers, but to my understanding, adding results didn't work for them previously either. It raises a NotImplementedError now.)

I feel it would be safer to compute the relative weight as W(T) / sum_T(W(T)) instead of using the number of trajectories.

Hmm unfortunately I think that this won't work. For the "improved sampling" case, the sum of the relative weights is not one.

@pmenczel pmenczel marked this pull request as ready for review April 4, 2024 05:37
Copy link
Member

@Ericgig Ericgig left a comment

Choose a reason for hiding this comment

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

It's nice to merge McResultImprovedSampling into McResult, but the overall complexity has increased... I believe it would be better to move MultiTrajResult to it's own file.

Did you review the dynamics-nmmonte,rst? Adding a note/example of using the improved sampling option there would be good.

Comment on lines +168 to +173
def merge(self, other, p=None):
raise NotImplementedError("Merging results of the stochastic solvers "
"is currently not supported. Please raise "
"an issue on GitHub if you would like to "
"see this feature.")

Copy link
Member

Choose a reason for hiding this comment

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

Why is that not supported?
There are no weight to them, it should be the easiest to merge...

Copy link
Member Author

@pmenczel pmenczel May 6, 2024

Choose a reason for hiding this comment

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

Currently, merging the measurement, dW etc attributes of stochastic results doesn't work, I think. That was the reason why I removed merge here, but maybe that went too far.

Should I just remove this, or maybe leave in a warning in the case a user tries to merge stochastic results where store_measurement was used?

Copy link
Member

Choose a reason for hiding this comment

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

It can be left as is for now. Merging measurement and dW should be easy, it can be done in another PR.

random_state = qutip.rand_ket(dim, seed=seed)
traj.add(t, random_state)

if time_dep_weights and np.random.randint(2):
Copy link
Member

Choose a reason for hiding this comment

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

Why is that options randomly used?
Having an options explicitly passed ignored randomly depending in a randint result feels strange.

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 wanted to add weights to only some of the trajectories, to test that nothing goes wrong if some of the traj don't have weights.

qutip/tests/solver/test_results.py Outdated Show resolved Hide resolved
qutip/tests/solver/test_results.py Outdated Show resolved Hide resolved
qutip/solver/result.py Outdated Show resolved Hide resolved
@@ -895,7 +970,31 @@ def __repr__(self):
lines.append(">")
return "\n".join(lines)

def __add__(self, other):
def merge(self, other, p=None):
Copy link
Member

Choose a reason for hiding this comment

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

The __add__ is already in the users hand and part of the official interface.
It is documented in the guide (and maybe the tutorials).
It's new, so it's probably not used much, but still we shouldn't remove it without a deprecation warning or a major release...

It would be better to keep it as merging with the default value of p.

Copy link
Member Author

Choose a reason for hiding this comment

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

The __add__ still exists, and it does call merge with the default value of p: https://github.com/pmenczel/qutip/blob/weighted-trajectories/qutip/solver/multitrajresult.py#L760

I introduced a new function merge because adding an optional parameter p to __add__ felt like a bad practice.

@pmenczel
Copy link
Member Author

pmenczel commented May 6, 2024

It's nice to merge McResultImprovedSampling into McResult, but the overall complexity has increased... I believe it would be better to move MultiTrajResult to it's own file.

Did you review the dynamics-nmmonte,rst? Adding a note/example of using the improved sampling option there would be good.

Thank you for the review. The complexity has increased, more than I expected when I started writing. I hope it is worth it?
I have extracted the MultiTrajResult and its subclasses to a new module, qutip.solver.multitrajresult.

I have added a mention about the improved sampling option in the guide, and I will have a look at the tutorial notebook whether it makes sense to include somewhere there.

Copy link
Member

@Ericgig Ericgig left a comment

Choose a reason for hiding this comment

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

I think the increased complexity is fine in this case.
Everything looks good. Thank you.

Comment on lines +168 to +173
def merge(self, other, p=None):
raise NotImplementedError("Merging results of the stochastic solvers "
"is currently not supported. Please raise "
"an issue on GitHub if you would like to "
"see this feature.")

Copy link
Member

Choose a reason for hiding this comment

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

It can be left as is for now. Merging measurement and dW should be easy, it can be done in another PR.

@pmenczel pmenczel merged commit 94f7392 into qutip:master May 7, 2024
11 of 12 checks passed
@pmenczel pmenczel deleted the weighted-trajectories branch May 7, 2024 04:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants