Search code examples
manimstochastic-process

Why is my path discontinuous when plotting a Brownian motion in manim?


I am trying to animate the path of a Brownian motion in manim, however my plot looks discontinuous whereas it should not be.

Here is the image (white path is the Brownian motion, green path is the quadratic variation): Brownian Path with unexpected discontinuties

I am using python 3.9.13 and manim CE 0.19.0. Here is the code

        from manim import *
        import numpy as np
    
        class BrownianPath():
          def __init__(self, drift = 0.0, sigma = 1.0):
              self.drift = drift
              self.sigma = sigma
              self.T = [0]
              self.path = [0]
              self.qv = [0]
              self.rng = np.random.default_rng()
    
          def increment(self, dT):
              self.path.append(self.path[-1] + self.drift*dT + self.rng.normal()*self.sigma*np.sqrt(dT))
              self.qv.append(self.qv[-1] + (self.path[-1] - self.path[-2])**2)
              self.T.append(self.T[-1] + dT)
    
          def run_for_time(self, T = 1.0, n = 100):
              for t in np.arange(0, T, T/n):
                  self.increment(T/n)
                  self.T.append(self.T[-1] + T/n)
    
          def get_last_path(self):
              return self.path[-1]
        
          def get_last_qv(self):
              return self.qv[-1]
    
          def get_last_T(self):
              return self.T[-1]
    
    
    class Brownian(Scene):
        def construct(self):
            axes = Axes([0, 5], [-1, 6])
            self.add(axes)
    
            # Create the brownian path
            bm = BrownianPath(drift=0.1)
    
            # Animate its path and its quadratic variation 
            self.path = VGroup()
            self.path.add(Line(axes.c2p(0, 0, 0), axes.c2p(0, 0, 0)))
    
            self.qv = VGroup()
            self.qv.add(Line(axes.c2p(0, 0, 0), axes.c2p(0, 0, 0)))
    
            def brownian_updater(mob, dt):
                bm.increment(dt)
                mob.move_to(axes.c2p(bm.get_last_T(), bm.get_last_path(), 0))
    
            def qv_updater(mob, dt):
                # assumes brownian updater called first (?)
                mob.move_to(axes.c2p(bm.get_last_T(), bm.get_last_qv(), 0))
    
            def add_path_element():
                last_line = self.path[-1]
                self.path.add(Line(last_line, axes.c2p(bm.get_last_T(), bm.get_last_path(), 0)))
                return self.path
    
            def add_qv_element():
                last_line = self.qv[-1]
                self.qv.add(Line(last_line, axes.c2p(bm.get_last_T(), bm.get_last_qv(), 0), color=GREEN))
                return self.qv
    
            dot = Dot(radius=0.07, color=BLUE)
            dot.move_to(axes.c2p(0, 0, 0))
    
            dot_qv = Dot(radius=0.08, color=GREEN)
            dot_qv.move_to(axes.c2p(0, 0, 0))
    
            dot.add_updater(brownian_updater)
            dot_qv.add_updater(qv_updater)
    
            curve_to_path = always_redraw(add_path_element)
            curve_to_qv = always_redraw(add_qv_element)    
    
            self.add(dot, dot_qv)
            self.add(curve_to_path, curve_to_qv)
            self.wait(5)

Perhaps it has something to do with how the scene calls the updaters, and the order is not consistent?


Solution

  • As you already found your way to Discord by now I suggest to continue the discussion over there, since the possibilities to include images and videos in answers and to online render code are better there.

    But as a short answer: the order of the execution of updaters depends on the order in which the corresponding objects are added to the scene.