diff --git a/verse/agents/example_agent/car_agent.py b/verse/agents/example_agent/car_agent.py
index abbaf36a92dad967e04e095db7e50d200336991e..c9fa3d9ef875e68ec2b075005efda4229c43acd7 100644
--- a/verse/agents/example_agent/car_agent.py
+++ b/verse/agents/example_agent/car_agent.py
@@ -7,6 +7,7 @@ from scipy.integrate import ode
 from verse import BaseAgent
 from verse import LaneMap
 from verse.analysis.utils import wrap_to_pi
+from verse.analysis.analysis_tree import TraceType
 from verse.parser import ControllerIR
 
 class NPCAgent(BaseAgent):
@@ -42,20 +43,19 @@ class NPCAgent(BaseAgent):
         a = 0
         return steering, a  
 
-    def TC_simulate(self, mode: List[str], initialCondition, time_bound, time_step, lane_map:LaneMap=None)->np.ndarray:
+    def TC_simulate(self, mode: List[str], initialCondition, time_bound, time_step, lane_map:LaneMap=None)->TraceType:
         time_bound = float(time_bound)
         number_points = int(np.ceil(time_bound/time_step))
-        t = [i*time_step for i in range(0,number_points)]
 
         init = initialCondition
-        trace = [[0]+init]
-        for i in range(len(t)):
+        trace = np.array([0, *init])
+        for i in range(number_points):
             steering, a = self.action_handler(mode, init, lane_map)
             r = ode(self.dynamic)    
             r.set_initial_value(init).set_f_params([steering, a])      
             res:np.ndarray = r.integrate(r.t + time_step)
-            init = res.flatten().tolist()
-            trace.append([t[i] + time_step] + init) 
+            init = res.flatten()
+            trace = np.vstack((trace, np.insert(init, 0, time_step * (i + 1))))
 
         return np.array(trace)
 
@@ -101,24 +101,22 @@ class CarAgent(BaseAgent):
         steering = np.clip(steering, -0.61, 0.61)
         return steering, a  
 
-    def TC_simulate(self, mode: List[str], initialCondition, time_bound, time_step, lane_map:LaneMap=None)->np.ndarray:
+    def TC_simulate(self, mode: List[str], initialCondition, time_bound, time_step, lane_map:LaneMap=None)->TraceType:
         time_bound = float(time_bound)
         number_points = int(np.ceil(time_bound/time_step))
-        t = [round(i*time_step,10) for i in range(0,number_points)]
 
         init = initialCondition
-        trace = [[0]+init]
-        for i in range(len(t)):
+        trace = np.array([0, *init])
+        for i in range(number_points):
             steering, a = self.action_handler(mode, init, lane_map)
             r = ode(self.dynamic)    
             r.set_initial_value(init).set_f_params([steering, a])      
             res:np.ndarray = r.integrate(r.t + time_step)
-            init = res.flatten().tolist()
+            init = res.flatten()
             if init[3] < 0:
                 init[3] = 0
-            trace.append([t[i] + time_step] + init) 
-
-        return np.array(trace)
+            trace = np.vstack((trace, np.insert(init, 0, time_step * (i + 1))))
+        return trace
 
 class WeirdCarAgent(CarAgent):
     def __init__(self, id, code = None, file_name = None):
diff --git a/verse/analysis/analysis_tree.py b/verse/analysis/analysis_tree.py
index ddb7532f9adf9b192cbbae126c238d1ea3d49e90..8e4420b43ae2b7ade1cc1b17b24171dce15e4e97 100644
--- a/verse/analysis/analysis_tree.py
+++ b/verse/analysis/analysis_tree.py
@@ -1,6 +1,13 @@
-from typing import List, Dict, Any
+from functools import reduce
+import pickle
+from typing import List, Dict, Any, Optional
 import json
 from treelib import Tree
+import numpy.typing as nptyp, numpy as np, portion
+
+from verse.analysis.dryvr import _EPSILON
+
+TraceType = nptyp.NDArray[np.float_]
 
 class AnalysisTreeNode:
     """AnalysisTreeNode class
@@ -26,7 +33,7 @@ class AnalysisTreeNode:
         type = 'simtrace',
         id = 0
     ):
-        self.trace:Dict = trace
+        self.trace: Dict[str, TraceType] = trace
         self.init: Dict[str, List[float]] = init
         self.mode: Dict[str, List[str]] = mode
         self.agent: Dict = agent
@@ -45,12 +52,12 @@ class AnalysisTreeNode:
             'parent': None, 
             'child': [], 
             'agent': {}, 
-            'init': self.init, 
-            'mode': self.mode,
+            'init': {aid: list(init) for aid, init in self.init.items()}, 
+            'mode': self.mode, 
             'height': self.height,
             'static': self.static, 
             'start_time': self.start_time,
-            'trace': self.trace, 
+            'trace': {aid: t.tolist() for aid, t in self.trace.items()}, 
             'type': self.type, 
             'assert_hits': self.assert_hits
         }
@@ -86,7 +93,7 @@ class AnalysisTreeNode:
     @staticmethod
     def from_dict(data) -> "AnalysisTreeNode":
         return AnalysisTreeNode(
-            trace = data['trace'],
+            trace = {aid: np.array(data['trace'][aid]) for aid in data["agent"].keys()},
             init = data['init'],
             mode = data['mode'],
             height = data['height'],
diff --git a/verse/analysis/incremental.py b/verse/analysis/incremental.py
index 82756bbb692a05ccc833637811a90d72f75dc4c8..5228d9019dab1b20d1a74c191f42c30c3b615146 100644
--- a/verse/analysis/incremental.py
+++ b/verse/analysis/incremental.py
@@ -5,7 +5,7 @@ from typing import Any, DefaultDict, List, Tuple, Optional, Dict
 from verse.agents.base_agent import BaseAgent
 from verse.analysis import AnalysisTreeNode
 from intervaltree import IntervalTree
-import itertools, copy, numpy as np
+import itertools, copy, numpy.typing as nptyp, numpy as np
 
 from verse.analysis.dryvr import _EPSILON
 # from verse.analysis.simulator import PathDiffs
@@ -21,7 +21,7 @@ class CachedTransition:
 
 @dataclass
 class CachedSegment:
-    trace: List[List[float]]
+    trace: nptyp.NDArray[np.float_]
     asserts: List[str]
     transitions: List[CachedTransition]
     run_num: int
@@ -97,7 +97,7 @@ def to_simulate(old_agents: Dict[str, BaseAgent], new_agents: Dict[str, BaseAgen
 
 def convert_sim_trans(agent_id, transit_agents, inits, transition, trans_ind):
     if agent_id in transit_agents:
-        return [CachedTransition(inits, trans_ind, mode, init, paths) for _id, mode, init, paths in transition]
+        return [CachedTransition({k: list(v) for k, v in inits.items()}, trans_ind, mode, list(init), paths) for _id, mode, init, paths in transition]
     else:
         return []
 
@@ -138,7 +138,7 @@ class SimTraceCache:
     def __init__(self):
         self.cache: DefaultDict[tuple, IntervalTree] = defaultdict(IntervalTree)
 
-    def add_segment(self, agent_id: str, node: AnalysisTreeNode, transit_agents: List[str], trace: List[List[float]], transition, trans_ind: int, run_num: int):
+    def add_segment(self, agent_id: str, node: AnalysisTreeNode, transit_agents: List[str], trace: nptyp.NDArray[np.float_], transition, trans_ind: int, run_num: int):
         key = (agent_id,) + tuple(node.mode[agent_id])
         init = node.init[agent_id]
         tree = self.cache[key]
diff --git a/verse/analysis/simulator.py b/verse/analysis/simulator.py
index a0a5a75c4b9455db3c3811530fa2625d2be49565..651a529e1e18d7cf67041e41bfbde294749f2027 100644
--- a/verse/analysis/simulator.py
+++ b/verse/analysis/simulator.py
@@ -13,7 +13,7 @@ from verse.parser.parser import ModePath, find
 pp = functools.partial(pprint.pprint, compact=True, width=130)
 
 # from verse.agents.base_agent import BaseAgent
-from verse.analysis.analysis_tree import AnalysisTreeNode, AnalysisTree
+from verse.analysis.analysis_tree import AnalysisTreeNode, AnalysisTree, TraceType
 
 PathDiffs = List[Tuple[BaseAgent, ModePath]]
 
@@ -29,7 +29,7 @@ class Simulator:
         self.cache_hits = (0, 0)
 
     @ray.remote
-    def simulate_one(config: "ScenarioConfig", cache, node: AnalysisTreeNode, later: int, remain_time: float, time_step: float, lane_map: LaneMap, run_num: int, past_runs: List[AnalysisTree], transition_graph: "Scenario") -> Tuple[int, int, List[AnalysisTreeNode], Dict[str, list], list]:
+    def simulate_one(config: "ScenarioConfig", cache, node: AnalysisTreeNode, later: int, remain_time: float, time_step: float, lane_map: LaneMap, run_num: int, past_runs: List[AnalysisTree], transition_graph: "Scenario") -> Tuple[int, int, List[AnalysisTreeNode], Dict[str, TraceType], list]:
         print(f"node id: {node.id}")
         cached_segments = {}
         cache_updates = []
@@ -58,10 +58,8 @@ class Simulator:
                 else:
                     # pp(("sim", agent_id, *mode, *init))
                     # Simulate the trace starting from initial condition
-                    trace = node.agent[agent_id].TC_simulate(
-                        mode, init, remain_time, time_step, lane_map)
+                    trace = node.agent[agent_id].TC_simulate(mode, init, remain_time, time_step, lane_map)
                     trace[:, 0] += node.start_time
-                    trace = trace.tolist()
                     node.trace[agent_id] = trace
         # pp(("cached_segments", cached_segments.keys()))
         # TODO: for now, make sure all the segments comes from the same node; maybe we can do
@@ -86,7 +84,8 @@ class Simulator:
         # pp(("next init:", {a: trace[transition_idx] for a, trace in node.trace.items()}))
 
         # truncate the computed trajectories from idx and store the content after truncate
-        truncated_trace, full_traces = {}, {}
+        truncated_trace: Dict[str, TraceType] = {}
+        full_traces: Dict[str, TraceType] = {}
         for agent_idx in node.agent:
             full_traces[agent_idx] = node.trace[agent_idx]
             if transitions:
@@ -151,7 +150,7 @@ class Simulator:
                 for agent_idx in next_node_agent:
                     if agent_idx not in next_node_init:
                         next_node_trace[agent_idx] = truncated_trace[agent_idx]
-                        next_node_init[agent_idx] = truncated_trace[agent_idx][0][1:]
+                        next_node_init[agent_idx] = truncated_trace[agent_idx][0][1:].tolist()
 
                 all_transition_paths.append(transition_paths)
                 tmp = AnalysisTreeNode(
@@ -293,7 +292,6 @@ class Simulator:
                 trace = node.agent[agent_id].TC_simulate(
                     mode, init, remain_time, time_step, lane_map)
                 trace[:, 0] += node.start_time
-                trace = trace.tolist()
                 node.trace[agent_id] = trace
             # pp(("cached_segments", cached_segments.keys()))
             # TODO: for now, make sure all the segments comes from the same node; maybe we can do