A dynamic spell cycle can be expressed as a Markov process. This means that we have a number of states in which we can be, a number of actions that can be performed in each state and transition probabilities that tell us given a current state and action how likely it is that we'll end in a given state. Once we have such a description our goal is to compute the various characteristics of the cycle such as dps and mps. To do this we compute the stationary distribution which tells us in the long run how likely we are to be in a given state at any point in time and use this to compute the expected values of different properties.
Let's start with a simple example to see how this all works out and compare the results with how you would otherwise solve the case in such a simple scenario. Imagine we have two spells, A and B. We usually cast A, but there is a 50% chance that A will result in a proc in which case we cast B (and this consumes the buff, B cannot proc the buff). We ignore any delays in observing the procs. To have a better grasp on what's going on lets make up some numbers. Lets say A has a 2 sec cast and does 100 damage and B has a 1 sec cast and does 200 damage.
I'll show two ways of how you can model this. In first case we'll define two states. S0 will represent a state where there is no proc, S1 will represent a state with proc buff. We can then write our model as
S0: A => S0 0.5 => S1 0.5 S1: B => S0 1
This means that in state S0 we perform action A and with probability 0.5 we end in state S0 and with probability 0.5 we end in state S1. In state S1 we perform action B and we always end in state S0.
Next we have to compute the stationary distribution. To compute this we use the fact that when we have stationary distribution and we move one step forward in the simulation we remain in stationary distribution (or in other terms we're looking for unit eigenvector associated with eigenvalue 1 if we express the transitions in a transition matrix). We do this by looking at how we can get into each of the states. We can get into state S0 with probability 0.5 if we are in S0 and with probability 1 if we are in S1. Similarly we can get into state S1 with probability 0.5 if we are in state S0. We can write this down as
S0 = 0.5 * S0 + S1
S1 = 0.5 * S0
At the same time we know that since this is a probability distribution the sum must be 1
S0 + S1 = 1
We now solve this set of equations (notice that one is redundant) and get the result
S0 = 2/3
S1 = 1/3
Once we have the stationary distribution we can start computing the values we want. Let's express expected value in general. Since in state S0 we always perform A and in state S1 we always perform B this is simply
value = S0 * value(A) + S1 * value(
We use this to compute both expected time and expected damage as
time = S0 * time(A) + S1 * time( = 2/3 * 2 sec + 1/3 * 1 sec = 1.67 sec
damage = 2/3 * 100 + 1/3 * 200 = 133.33
And finally the dps of this cycle is 80.
We can express this cycle in another way by realizing that if A procs, we always follow with B. Looking at it in this way we can express the cycle in a one-state model.
S0: A => S0 0.5 AB => S0 0.5
Stationary distribution here is very simple since we're always in state S0. Let's look at the values now.
time = 0.5 * time(A) + 0.5 * time(AB) = 0.5 * time(A) + 0.5 * time(A) + 0.5 * time( = 2.5 sec
damage = damage(A) + 0.5 * damage( = 200
And finally dps = 200 / 2.5 = 80.
This final model is also very easy to see how you would usually approximate a dynamic cycle with a static one. Since half the time you cast A and half the time you cast AB this is basically equivalent to AAB. And for this static cycle we know its cast time is 5 sec with 400 damage which again gives 80 dps.
Hopefully this simple example was enough to help you work through the following examples. First we will look at AB-ABar cycle where we replace AB with MBAM on proc. We will use two states, in S0 the last ABar did not proc MB, in S1 the last ABar procced MB.
S0: AB-ABar1 => S0 0.8 * 0.8 => S1 1 - 0.8 * 0.8 S1: MBAM-ABar => S0 0.8 => S1 0.2
We solve for stationary distribution:
S0 = 0.64 * S0 + 0.8 * S1
S1 = 0.36 * S0 + 0.2 * S2
S0 + S1 = 1
S0 = 0.69
S1 = 0.31
And the values are
value = 0.69 * value(AB-ABar1) + 0.31 * value(MBAM-ABar)
If we had damage and timing information for the spells it would be simple then to compute the expected time and damage of the action in a state and to get the dps. Note that to compute mps you do exactly the same but replace damage with mana.
Next lets look at ABx3-ABar. We have to be very specific when specifying the spell selection. For this cycle if ABar procs we always follow it with MBAM. If first AB procs we cast MBAM as soon as we notice the proc (that is after second AB) and follow MBAM with ABar. Similar if second AB procs we cast MBAM-ABar after the third AB. In both cases if the ABar after MBAM procs we follow with another MBAM. If third AB procs we won't notice it until after ABar. The easiest way to express all this is with an expanded one-state model.
S0: AB0-AB1-AB2-ABar3 => S0 0.8 * 0.8 * 0.8 * 0.8 AB0-AB1-AB2-ABar3-MBAM => S0 0.8 * 0.8 * (1 - 0.8 * 0.8) AB0-AB1-AB2-MBAM3-ABar => S0 0.8 * 0.2 * 0.8 AB0-AB1-AB2-MBAM3-ABar-MBAM => S0 0.8 * 0.2 * 0.2 AB0-AB1-MBAM2-ABar => S0 0.2 * 0.8 AB0-AB1-MBAM2-ABar-MBAM => S0 0.2 * 0.2
This looks a bit scary so let's try to simplify it a bit
value = value(AB0) + value(AB1) + 0.8 * value(AB2) + 0.36 * value(ABar) + 0.64 * value(ABar3) + 0.2 * value(MBAM2) + 0.16 * value(MBAM3) + 0.3024 * value(MBAM)
If we wanted to do further analysis with this we could then take into account that cast time is the same regardless of number of debuffs present and the various damage relations between spells with different number of debuffs up.
Last lets look at the AB spam with MBAM whenever it procs (with delay of one AB to notice the proc). We'll use two states to model this. State S0 is the ramp up state with 0 stacks of AB debuff. State S1 is the ramped up state with 3 stacks of AB debuff.
S0: AB0-AB1-AB2 => S1 0.8 * 0.8 * 0.8 AB0-AB1-AB2-AB3-MBAM3 => S0 0.8 * 0.8 * 0.2 AB0-AB1-AB2-MBAM3 => S0 0.8 * 0.2 AB0-AB1-MBAM2 => S0 0.2 S1: AB3 => S1 0.8 AB3-AB3-MBAM3 => S0 0.2
We compute the stationary distribution:
S0 = 0.488 * S0 + 0.2 * S1
S1 = 0.512 * S0 + 0.8 * S1
S0 + S1 = 1
S0 = 0.28
S1 = 0.72
And finally the expected value:
value = 0.28 * (0.512 * value(AB0-AB1-AB2) + 0.128 * value(AB0-AB1-AB2-AB3-MBAM3) + 0.16 * value(AB0-AB1-AB2-MBAM3) + 0.2 * value(AB0-AB1-MBAM2)) + 0.72 * (0.8 * value(AB3) + 0.2 * value(AB3-AB3-MBAM3))
Or in a simpler way
value = 0.576 * value(AB3) + 0.28 * (0.512 * value(AB0-AB1-AB2-AB3-AB3-MBAM3) + 0.128 * value(AB0-AB1-AB2-AB3-MBAM3) + 0.16 * value(AB0-AB1-AB2-MBAM3) + 0.2 * value(AB0-AB1-MBAM2))
Hopefully this will be enough information to give you an idea how you can compare dynamic cycles analytically. So if you have an idea for a cool new cycle that noone else thought of before, this would be how you can start to compare it to others.