-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
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
Add states/nodes to MarkovChain/DiGraph #237
Conversation
152505c
to
b01b0ab
Compare
@oyamad Many thanks for working on this! I get the following:
I think the problem is that below this line we need to test whether there are labels. To tell the truth, I think there is quite a bit of complexity just because we want to have properties. Once we have them, there are two different ways to access information (attribute or method), which is in itself slightly "unpythonic". (There should be only one way to do a give task, I mean.) But of course you should be the judge and I'm happy to go along with what you decide. |
@jstac Thanks for testing the code. My apologies, I had secretly made a forced push at some point which adds a checking here. Now it should work fine. For the issue property versus method, I agree the current version is not pythonic, whereas at the same time I think I would like to maintain the idea that the connectedness and cyclicity structures are the properties of a digraph. We can discuss this again tomorrow. |
@jstac We have already discussed and I know you don't like this, but to share with other people: |
Properties return indices as before
I think I have made up my mind.
|
TODO
|
This is ready for review. |
@oyamad Thanks for all this hard work! I'm looking at the code now. In the meantime, I know we discussed something similar before but I would like to raise the topic again: I think the DiGraph below should be aperiodic: (Discussion continues after this code.)
For the definitions I know related to Markov chains, like this one, both states are aperiodic, and hence the chain is aperiodic. I'm less familiar with graph theory, but don't these two states each have a cycle of length 1, and therefore the graph is aperiodic? I'm using this reference for my definitions: http://carmenere.ucsd.edu/jorge/teaching/mae247/s13/notes/lec03-graph-theory.pdf |
In
|
OK, so there are some things about the implementation that I don't agree with. Consider
I feel like this is already surprising for the user, since the output doesn't match the state values. This is a problem caused by the existence of attributes. Now consider
So perhaps the user looks up the documentation to try to get output that matches his/her supplied state values. At the moment the
It seems strange to have to supply In summary, I think there are several possible sources of confusion for the user. All of them are related to the coexistence of properties and methods. I also think it's confusing for some users even just to have get methods along with similar attributes. What I propose is this: For attributes that do not return states (e.g., For attributes that give back indices/states, I propose the class supplies only methods (same for DiGraph and MarkovChain). These methods have an optional argument I realize you disagree on these points but I really think my suggestions will make it easier for the user. |
@jstac Thanks for testing the code. For |
The adjacent matrix of a
We can give it an additional name |
As to periodicity, I have to look into the references I used, to recall what my reasoning was. (If you follow the definition in Wikipedia, the period of state 0 for I also have to look up the implementation issues. |
Periodicity is a separate issue. Let's discuss it in Issue #245. |
Another issue: In [2]: mc = MarkovChain([[0, 0, 1], [0, 1, 0], [1, 0, 0]], state_values=[1, 2, 3])
In [3]: mc.simulate(10, init=1)
Out[3]: array([2, 2, 2, 2, 2, 2, 2, 2, 2, 2]) Should we add an argument |
There is an ambiguity if Should we not allow the |
and remove `return_values` from `simulate`
As for 3, I am actually indifferent between this and adding an def simulate(self, ts_length, init=None, num_reps=None,
return_values=True, init_value=None, random_state=None):
... |
@oyamad Please give me a bit longer to respond. I'm tied up with some other stuff but this is very important code, and we want to design the best interface possible. |
@oyamad I'm sorry I've taken so long to get to this and even now I don't have a lot of time to discuss. I do like it as is. It's nice to have properties, and efficient when useful state is preserved. But I think the interface could be a little clearer. I like the Apple philosophy regarding interfaces: there's no need to read documentation unless you want to do something unusual. I also like the "principle of least surprises". As is, I can imagine that someone will type So my preference would be to drop the mc.communication_classes
mc.communication_classes_as_indices
mc.recurrent_classes
mc.recurrent_classes_as_indices
mc.cyclic_classes
mc.cyclic_classes_as_indices If I now type Regarding By the way, I currently get this error. In [25]: P = np.asarray([ [0.5, 0.5], [0.4, 0.6] ])
In [26]: mc = qe.MarkovChain(P, state_values=(10, 20))
In [27]: mc.simulate_values(10)
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
/home/john/sync_dir/quantecon/QuantEcon.py/quantecon/markov/core.py in _get_index(self, value)
291 try:
--> 292 idx = np.where(self.state_values == value)[0][0]
293 return idx
IndexError: index 0 is out of bounds for axis 0 with size 0
During handling of the above exception, another exception occurred:
ValueError Traceback (most recent call last)
<ipython-input-27-023d194e5620> in <module>()
----> 1 mc.simulate_values(10)
/home/john/sync_dir/quantecon/QuantEcon.py/quantecon/markov/core.py in simulate_values(self, ts_length, init_value, num_reps, random_state)
558
559 """
--> 560 init_idx = self.get_index(init_value)
561 X = self.simulate(ts_length, init=init_idx, num_reps=num_reps,
562 random_state=random_state)
/home/john/sync_dir/quantecon/QuantEcon.py/quantecon/markov/core.py in get_index(self, value)
253
254 if values.ndim <= state_values_ndim - 1:
--> 255 return self._get_index(value)
256 elif values.ndim == state_values_ndim: # array of values
257 k = values.shape[0]
/home/john/sync_dir/quantecon/QuantEcon.py/quantecon/markov/core.py in _get_index(self, value)
293 return idx
294 except IndexError:
--> 295 raise ValueError(error_msg)
296 else:
297 idx = 0
ValueError: value None not found |
Test should fail for this commit
- Change *_components to return labels - Remove get_*_components
- Change *_classes to return values - Remove get_*_classes
- Add simulate_indices which return indices
@jstac Thank you for your comments. I followed your opinion regarding the properties.
|
@oyamad Thanks! The code is beautiful, as usual. And the interface is great. Regarding point 2, it should be just as you say. I think this is ready to merge. |
@jstac Thanks, I think we have reached a good solution. |
@oyamad Me too. Thanks for all the hard work! Are you happy for me to merge? I think we can go ahead. |
Yes, please do merge. |
🎉 Good work! This was a great example of open source development |
This will close #101.