rvanvenetie / spacetime Goto Github PK
View Code? Open in Web Editor NEWThis code supplements arXiv:2104.08143, where we describe an adaptive method for parabolic evolution equations.
License: MIT License
This code supplements arXiv:2104.08143, where we describe an adaptive method for parabolic evolution equations.
License: MIT License
Ideas:
Once the spacetime code is working, we need to cleanup this beast.
The spacetime applicator is very slow. Some parts can be optimized using simple caching, others require more thinking.
Specifically, the space applicator is slow because of creating triangulationviews, etc.
Total time: 47.4894 s
File: /Users/raymond/Projects/spacetime/Python/applications/heat_equation_test.py
Function: test_heat_error_reduction at line 226
Line # Hits Time Per Hit % Time Line Contents
==============================================================
226 @pytest.mark.slow
227 @profile
228 def test_heat_error_reduction():
229 1 6.0 6.0 0.0 max_level = 10
230 # Create space part.
231 1 601.0 601.0 0.0 triang = InitialTriangulation.unit_square()
232 1 428861.0 428861.0 0.9 triang.vertex_meta_root.uniform_refine(max_level + 2)
233 1 71.0 71.0 0.0 basis_space = HierarchicalBasisFunction.from_triangulation(triang)
234 1 476890.0 476890.0 1.0 basis_space.deep_refine()
235
236 # Create time part for X^\delta
237 1 5.0 5.0 0.0 basis_time = ThreePointBasis()
238 1 175187.0 175187.0 0.4 basis_time.metaroot_wavelet.uniform_refine(max_level)
239
240 1 6.0 6.0 0.0 n_t = 9
241 1 23.0 23.0 0.0 errors_quad = []
242 1 4.0 4.0 0.0 ndofs = []
243 1 2.0 2.0 0.0 time_per_dof = []
244 1 61.0 61.0 0.0 np.set_printoptions(precision=4)
245 1 26.0 26.0 0.0 np.set_printoptions(linewidth=10000)
246 1 6.0 6.0 0.0 for level in range(6, max_level, 2):
247 # Create X^\delta as a sparse grid.
248 1 4.0 4.0 0.0 X_delta = DoubleTree.from_metaroots(
249 1 69.0 69.0 0.0 (basis_time.metaroot_wavelet, basis_space.root))
250 1 119463.0 119463.0 0.3 X_delta.sparse_refine(level, weights=[2, 1])
251 1 5784.0 5784.0 0.0 ndofs.append(len(X_delta.bfs()))
252 1 5.0 5.0 0.0 print('X_delta: dofs time axis={}\tdofs space axis={}'.format(
253 1 3097.0 3097.0 0.0 len(X_delta.project(0).bfs()), len(X_delta.project(1).bfs())))
254
255 # Create heat equation object.
256 1 369420.0 369420.0 0.8 heat_eq = HeatEquation(X_delta=X_delta)
257 1 1682806.0 1682806.0 3.5 rhs = example_rhs(heat_eq)
258
259 # Now actually solve this beast!
260 1 44227050.0 44227050.0 93.1 sol, num_iters = heat_eq.solve(rhs)
261 residual_norm = np.linalg.norm(
262 heat_eq.mat.apply(sol).to_array() - rhs.to_array())
263 print('MINRES solved in {} iterations with a residual norm {}'.format(
264 num_iters, residual_norm))
265 print('Time per dof is approximately {}'.format(
266 heat_eq.time_per_dof()))
267 time_per_dof.append(heat_eq.time_per_dof())
268
269 u, u_order, u_slice_norm = example_solution_function()
270
271 cur_errors_quad = np.ones(n_t)
272 for i, t in enumerate(np.linspace(0, 1, n_t)):
273 sol_slice = sol[1].slice(i=0,
274 coord=t,
275 slice_cls=TriangulationFunction)
276 cur_errors_quad[i] = sol_slice.error_L2(
277 lambda xy: u[0](t) * u[1](xy),
278 u_slice_norm(t),
279 u_order[1],
280 )
281
282 errors_quad.append(cur_errors_quad)
283 rates_quad = np.log(errors_quad[-1] / errors_quad[0]) / np.log(
284 ndofs[0] / ndofs[-1])
285
286 print('-- Results for level = {} --'.format(level))
287 print('\tdofs:', ndofs[-1])
288 print('\ttime_per_dof: {0:.4f}'.format(time_per_dof[-1]))
289 print('\terrors:', cur_errors_quad)
290 print('\trates:', rates_quad)
291 print('\n')
Total time: 43.9371 s
File: /Users/raymond/Projects/spacetime/Python/datastructures/applicator.py
Function: apply at line 151
Line # Hits Time Per Hit % Time Line Contents
==============================================================
151 @profile
152 def apply(self, vec):
153 """ Applies this block-bilinear form the given input vectors.
154
155 Arguments:
156 vec: (vec_0, vec_1) a block-vector on Z_0 x Z_1.
157 """
158 8 15.0 1.9 0.0 assert isinstance(vec, BlockTreeVector)
159 8 8112691.0 1014086.4 18.5 out_0 = self.applicators[0][0].apply(vec[0])
160 8 15947856.0 1993482.0 36.3 out_0 += self.applicators[0][1].apply(vec[1])
161
162 8 13614890.0 1701861.2 31.0 out_1 = self.applicators[1][0].apply(vec[0])
163 7 6261599.0 894514.1 14.3 out_1 += self.applicators[1][1].apply(vec[1])
164
165 7 82.0 11.7 0.0 return BlockTreeVector((out_0, out_1))
Total time: 44.2122 s
File: /Users/raymond/Projects/spacetime/Python/datastructures/applicator.py
Function: _matvec at line 194
Line # Hits Time Per Hit % Time Line Contents
==============================================================
194 @profile
195 def _matvec(self, x):
196 8 49.0 6.1 0.0 time_begin = time.process_time()
197
198 8 184770.0 23096.2 0.4 self.input_vec.from_array(x)
199 8 43937257.0 5492157.1 99.4 res_tree = self.applicator.apply(self.input_vec)
200 7 90048.0 12864.0 0.2 result = res_tree.to_array()
201
202 7 67.0 9.6 0.0 self.total_time += time.process_time() - time_begin
203 7 15.0 2.1 0.0 self.total_applies += 1
204 7 3.0 0.4 0.0 return result
Total time: 12.979 s
File: /Users/raymond/Projects/spacetime/Python/datastructures/multi_tree_view.py
Function: _refine at line 112
Line # Hits Time Per Hit % Time Line Contents
==============================================================
112 @profile
113 def _refine(self,
114 i,
115 children=None,
116 call_filter=None,
117 make_conforming=False):
118 """ Refines the node in the `i`-th coordinate.
119
120 If the node that will be introduced has multiple parents, then these
121 must be brothers of self (and already exist).
122
123 Args:
124 i: The axis we are considering.
125 children: If set, the list of children to create. If none, refine
126 all children that exist in the underlying tree.
127 call_filter: This function can be used to filter children. It is
128 called with the multi-node that is to be created.
129 """
130 349269 1118420.0 3.2 8.6 if self.is_full(i): return self._children[i]
131 269287 269398.0 1.0 2.1 if children is None: children = self.nodes[i].children
132 269287 258473.0 1.0 2.0 if call_filter is None: call_filter = lambda _: True
133
134 610224 594618.0 1.0 4.6 for child_i in children:
135 # If this child does not exist in underlying tree, we can stop.
136 340938 543852.0 1.6 4.2 if child_i not in self.nodes[i].children: continue
137
138 340938 722020.0 2.1 5.6 child_nodes = _replace(i, self.nodes, child_i)
139
140 # Skip if this child already exists, or if the filter doesn't pass.
141 340938 1668732.0 4.9 12.9 if child_nodes in (n.nodes for n in self._children[i]) \
142 279502 690630.0 2.5 5.3 or not call_filter(child_nodes):
143 98692 84711.0 0.9 0.7 continue
144
145 # Ensure all the parents of the to be created child exist.
146 # These are brothers in various axis of the current node.
147 180810 166423.0 0.9 1.3 brothers = []
148 458167 548074.0 1.2 4.2 for j in range(self.dim):
149 277357 253668.0 0.9 2.0 brothers.append([
150 277357 291749.0 1.1 2.2 self._find_brother(
151 _replace(j, child_nodes, child_parent_j), j, i,
152 make_conforming)
153 277357 2204697.0 7.9 17.0 for child_parent_j in child_nodes[j].parents
154 ])
155
156 180809 1785655.0 9.9 13.8 child = self.__class__(nodes=child_nodes, parents=brothers)
157 458164 526327.0 1.1 4.1 for j in range(self.dim):
158 585017 590267.0 1.0 4.5 for brother in brothers[j]:
159 307662 378087.0 1.2 2.9 brother._children[j].append(child)
160
161 269286 283166.0 1.1 2.2 return self._children[i]
Total time: 7.12491 s
File: /Users/raymond/Projects/spacetime/Python/datastructures/multi_tree_view.py
Function: _deep_refine at line 234
Line # Hits Time Per Hit % Time Line Contents
==============================================================
234 @profile
235 def _deep_refine(self, call_filter=None, call_postprocess=None):
236 """ Deep-refines `self` by recursively refining the multitree.
237
238 Args:
239 call_filter: This call determines whether a given multinode
240 should be inside the subtree.
241 call_postprocess: This call will be invoked with a freshly
242 created multinode object. Can be used to load data, etc.
243 """
244 875 958.0 1.1 0.0 if call_postprocess is None: call_postprocess = lambda _: None
245 875 679.0 0.8 0.0 my_nodes = []
246 875 1182.0 1.4 0.0 queue = deque([self])
247 70274 46093.0 0.7 0.6 while queue:
248 69399 52155.0 0.8 0.7 my_node = queue.popleft()
249 69399 55415.0 0.8 0.8 if my_node.marked: continue
250 64835 49957.0 0.8 0.7 my_nodes.append(my_node)
251 64835 361059.0 5.6 5.1 call_postprocess(my_node)
252 64835 49693.0 0.8 0.7 my_node.marked = True
253 130190 125507.0 1.0 1.8 for i in range(self.dim):
254 65355 41918.0 0.6 0.6 queue.extend(
255 65355 48026.0 0.7 0.7 my_node._refine(i=i,
256 65355 37710.0 0.6 0.5 call_filter=call_filter,
257 65355 6167355.0 94.4 86.6 make_conforming=True))
258
259 65710 40240.0 0.6 0.6 for my_node in my_nodes:
260 64835 46959.0 0.7 0.7 my_node.marked = False
Total time: 15.7844 s
File: /Users/raymond/Projects/spacetime/Python/space/applicator.py
Function: apply at line 19
Line # Hits Time Per Hit % Time Line Contents
==============================================================
19 @profile
20 def apply(self, vec_in, vec_out):
21 """ Apply the multiscale operator. """
22 1447 392314.0 271.1 2.5 vec_in_nodes = vec_in.bfs()
23 1447 285316.0 197.2 1.8 vec_out_nodes = vec_out.bfs()
24 1447 2278.0 1.6 0.0 if len(vec_in_nodes) == 0 or len(vec_out_nodes) == 0: return
25 1171 48860.0 41.7 0.3 if [n.node for n in vec_in_nodes] == [n.node for n in vec_out_nodes]:
26 # This is the case where vec_in == vec_out, i.e. symmetric.
27 872 9116427.0 10454.6 57.8 self.operator.triang = TriangulationView(vec_in)
28 872 305602.0 350.5 1.9 np_vec_in = vec_in.to_array()
29 872 1310412.0 1502.8 8.3 np_vec_out = self.operator.apply(np_vec_in)
30 872 304077.0 348.7 1.9 vec_out.from_array(np_vec_out)
31 872 1056.0 1.2 0.0 return vec_out
32
33 # This is the case where vec_in != vec_out.
34 # We can handle this by enlarging vec_in and vec_out.
35 299 316.0 1.1 0.0 def call_copy(my_node, other_node):
36 if not my_node.is_metaroot():
37 my_node.value = other_node.value
38
39 # If that's not the case, create an enlarged vec_in.
40 299 9812.0 32.8 0.1 vec_in_new = TreeVector.from_metaroot(vec_in.node)
41 299 1905307.0 6372.3 12.1 vec_in_new.union(vec_in, call_postprocess=call_copy)
42 299 202843.0 678.4 1.3 vec_in_new.union(vec_out, call_postprocess=None)
43
44 # Also create an enlarge vec_out.
45 299 9849.0 32.9 0.1 vec_out_new = TreeVector.from_metaroot(vec_in.node)
46 299 1393367.0 4660.1 8.8 vec_out_new.union(vec_in, call_postprocess=None)
47 299 205388.0 686.9 1.3 vec_out_new.union(vec_out, call_postprocess=None)
48
49 # Apply for these enlarged vectors.
50 299 971.0 3.2 0.0 self.apply(vec_in_new, vec_out_new)
51
52 # Now copy only specific parts back into vec_out, mark these nodes.
53 299 553.0 1.8 0.0 vec_out.union(vec_out_new,
54 299 348.0 1.2 0.0 call_filter=lambda _: False,
55 299 289023.0 966.6 1.8 call_postprocess=call_copy)
56
57 299 294.0 1.0 0.0 return vec_out
Total time: 8.50112 s
File: /Users/raymond/Projects/spacetime/Python/space/triangulation_view.py
Function: __init__ at line 29
Line # Hits Time Per Hit % Time Line Contents
==============================================================
29 @profile
30 def __init__(self, vertex_view):
31 """ Initializer given a vertex (sub)tree. """
32 872 3041.0 3.5 0.0 if isinstance(vertex_view, NodeViewInterface):
33 573 4883.0 8.5 0.1 assert vertex_view.is_metaroot()
34
35 # Store the vertices inside the vertex_view
36 872 233930.0 268.3 2.8 self.vertices = vertex_view.bfs()
37 872 1367.0 1.6 0.0 assert self.vertices
38
39 # In the case of stacked views, we must iterate to find the vertices.
40 2616 7851.0 3.0 0.1 while isinstance(self.vertices[0], NodeViewInterface):
41 1744 35780.0 20.5 0.4 self.vertices = [v.node for v in self.vertices]
42 872 1262.0 1.4 0.0 assert isinstance(self.vertices[0], Vertex)
43
44 # Extract the original element root.
45 872 2565.0 2.9 0.0 elem_meta_root = self.vertices[0].patch[0].parent
46 872 1217.0 1.4 0.0 assert isinstance(elem_meta_root, MetaRoot)
47 872 1449.0 1.7 0.0 assert isinstance(elem_meta_root.children[0], Element2D)
48
49 # Mark all necessary vertices -- uses the mark field inside vertex.
50 22652 29109.0 1.3 0.3 for idx, vertex in enumerate(self.vertices):
51 21780 34191.0 1.6 0.4 assert not vertex.is_metaroot()
52 21780 27740.0 1.3 0.3 vertex.marked = idx
53
54 # Two helper functions used inside the element tree generation..
55 872 1206.0 1.4 0.0 def newest_vertex_in_tree_view(elem):
56 """ Does the given element have all vertices in our subtree. """
57 return not isinstance(elem.newest_vertex().marked, bool)
58
59 872 1149.0 1.3 0.0 def store_vertices_element_view(elem_view):
60 """ Store vertex view indices inside the element_view object. """
61 if not isinstance(elem_view.node, MetaRoot):
62 elem_view.vertices_view_idx = [
63 v.marked for v in elem_view.node.vertices
64 ]
65
66 # Now create the associated element tree.
67 872 19814.0 22.7 0.2 self.elem_tree_view = TreeView(ElementView(elem_meta_root))
68 872 1505.0 1.7 0.0 self.elem_tree_view.deep_refine(
69 872 1096.0 1.3 0.0 call_filter=newest_vertex_in_tree_view,
70 872 7137981.0 8185.8 84.0 call_postprocess=store_vertices_element_view)
71
72 # Unmark the vertices
73 22652 27833.0 1.2 0.3 for vertex in self.vertices:
74 21780 28033.0 1.3 0.3 vertex.marked = False
75
76 # Also store a flattened list of the elements.
77 872 364725.0 418.3 4.3 self.elements = self.elem_tree_view.bfs()
78 872 1332.0 1.5 0.0 assert self.elements
79
80 # Create the history object -- uses mark field of the vertex view obj.
81 872 1240.0 1.4 0.0 self.history = []
82 60072 74361.0 1.2 0.9 for elem in self.elements:
83 59200 102877.0 1.7 1.2 vertex = self.vertices[elem.newest_vertex()]
84 59200 186723.0 3.2 2.2 if elem.level == 0 or vertex.marked: continue
85 18292 24101.0 1.3 0.3 vertex.marked = True
86 18292 37074.0 2.0 0.4 assert len(elem.parents) == 1
87 18292 45941.0 2.5 0.5 self.history.append((elem.newest_vertex(), elem.parents[0]))
88
89 872 1418.0 1.6 0.0 assert len(self.history) == len(self.vertices) - len(
90 872 1891.0 2.2 0.0 self.vertices[0].parents[0].children)
91
92 # Undo marking.
93 22652 27865.0 1.2 0.3 for vertex in self.vertices:
94 21780 28574.0 1.3 0.3 vertex.marked = False
Total time: 42.9673 s
File: /Users/raymond/Projects/spacetime/Python/spacetime/applicator.py
Function: apply at line 107
Line # Hits Time Per Hit % Time Line Contents
==============================================================
107 @profile
108 def apply(self, vec):
109 """ Apply the tensor product applicator to the given vector. """
110 # Assert that vec is defined on Lambda_in
111 46 62.0 1.3 0.0 assert all(n1.nodes == n2.nodes
112 46 453630.0 9861.5 1.1 for n1, n2 in zip(vec.bfs(), self.Lambda_in.bfs()))
113
114 # Shortcut for clarity.
115 46 95.0 2.1 0.0 vec_in = vec
116
117 # Create two empty out vectors for the L and U part.
118 46 3492560.0 75925.2 8.1 vec_out_low = self.Lambda_out.deep_copy(mlt_tree_cls=DoubleTreeVector)
119 46 3768375.0 81921.2 8.8 vec_out_upp = self.Lambda_out.deep_copy(mlt_tree_cls=DoubleTreeVector)
120
121 # Calculate R_sigma(Id x A_1)I_Lambda
122 46 3763938.0 81824.7 8.8 sigma = self.sigma()
123 621 8795.0 14.2 0.0 for psi_in_labda in sigma.project(0).bfs():
124 575 15097.0 26.3 0.0 fiber_in = vec_in.fiber(1, psi_in_labda)
125 575 2605.0 4.5 0.0 fiber_out = sigma.fiber(1, psi_in_labda)
126 575 9698613.0 16867.2 22.6 self.applicator_space.apply(fiber_in, fiber_out)
127
128 # Calculate R_Lambda(L_0 x Id)I_Sigma
129 13340 182604.0 13.7 0.4 for psi_out_labda in vec_out_low.project(1).bfs():
130 13294 73938.0 5.6 0.2 fiber_in = sigma.fiber(0, psi_out_labda)
131 13294 294367.0 22.1 0.7 fiber_out = vec_out_low.fiber(0, psi_out_labda)
132 13294 2951703.0 222.0 6.9 self.applicator_time.apply_low(fiber_in, fiber_out)
133
134 # Calculate R_Theta(U_1 x Id)I_Lambda
135 46 6772692.0 147232.4 15.8 theta = self.theta()
136 13050 173861.0 13.3 0.4 for psi_in_labda in theta.project(1).bfs():
137 13005 71519.0 5.5 0.2 fiber_in = vec_in.fiber(0, psi_in_labda)
138 13005 56701.0 4.4 0.1 fiber_out = theta.fiber(0, psi_in_labda)
139 13005 3593731.0 276.3 8.4 self.applicator_time.apply_upp(fiber_in, fiber_out)
140
141 # Calculate R_Lambda(id x A2)I_Theta
142 618 8549.0 13.8 0.0 for psi_out_labda in vec_out_upp.project(0).bfs():
143 573 4308.0 7.5 0.0 fiber_in = theta.fiber(1, psi_out_labda)
144 573 224749.0 392.2 0.5 fiber_out = vec_out_upp.fiber(1, psi_out_labda)
145 573 6116572.0 10674.6 14.2 self.applicator_space.apply(fiber_in, fiber_out)
146
147 # Sum and return the results.
148 45 35.0 0.8 0.0 vec_out = vec_out_low
149 45 1238111.0 27513.6 2.9 vec_out += vec_out_upp
150 45 50.0 1.1 0.0 return vec_out
Total time: 3.63263 s
File: /Users/raymond/Projects/spacetime/Python/time/applicator.py
Function: _initialize at line 30
Line # Hits Time Per Hit % Time Line Contents
==============================================================
30 @profile
31 def _initialize(self, vec_in, vec_out):
32 """ Helper function to initialize fields in datastructures. """
33
34 # Sanity check that we start with an empty vector
35 26299 45464.0 1.7 1.3 if isinstance(vec_out, NodeViewInterface):
36 49242 711460.0 14.4 19.6 for nv in vec_out.bfs():
37 22943 28016.0 1.2 0.8 assert nv.value == 0
38 else:
39 for psi, value in vec_out.items():
40 assert value == 0
41
42 26299 941108.0 35.8 25.9 self.Lambda_in = MultiscaleFunctions(vec_in)
43 26299 1029677.0 39.2 28.3 self.Lambda_out = MultiscaleFunctions(vec_out)
44
45 # Store the vector inside the wavelet tree.
46 26299 47417.0 1.8 1.3 if isinstance(vec_in, NodeViewInterface):
47 41015 585308.0 14.3 16.1 for nv in vec_in.bfs():
48 14716 24364.0 1.7 0.7 assert nv.node.coeff[0] == 0
49 14716 30279.0 2.1 0.8 nv.node.coeff[0] = nv.value
50 else:
51 for psi, value in vec_in.items():
52 assert psi.coeff[0] == 0
53 psi.coeff[0] = value
54
55 # Last, update the fields inside the elements.
56 41015 50624.0 1.2 1.4 for psi in self.Lambda_in:
57 35708 23789.0 0.7 0.7 for elem in psi.support:
58 20992 13312.0 0.6 0.4 elem.Lambda_in = True
59
60 49242 46477.0 0.9 1.3 for psi in self.Lambda_out:
61 55893 35569.0 0.6 1.0 for elem in psi.support:
62 32950 19762.0 0.6 0.5 elem.Lambda_out = True
Total time: 1.12214 s
File: /Users/raymond/Projects/spacetime/Python/time/applicator.py
Function: _finalize at line 64
Line # Hits Time Per Hit % Time Line Contents
==============================================================
64 @profile
65 def _finalize(self, vec_in, vec_out):
66 """ Helper function to finalize the results.
67
68 This also copies the data from the single trees into vec_out. """
69
70 # Copy result into vec_out
71 26299 56793.0 2.2 5.1 if isinstance(vec_out, NodeViewInterface):
72 49242 709730.0 14.4 63.2 for nv in vec_out.bfs():
73 22943 51744.0 2.3 4.6 nv.value = nv.node.coeff[1]
74 else:
75 # TODO: This should be removed
76 for psi in self.Lambda_out:
77 vec_out[psi] = psi.coeff[1]
78
79 # Delete the used fields in the Element
80 41015 47416.0 1.2 4.2 for psi in self.Lambda_in:
81 35708 21925.0 0.6 2.0 for elem in psi.support:
82 20992 12222.0 0.6 1.1 elem.Lambda_in = False
83 49242 43732.0 0.9 3.9 for psi in self.Lambda_out:
84 55893 32763.0 0.6 2.9 for elem in psi.support:
85 32950 18317.0 0.6 1.6 elem.Lambda_out = False
86
87 # Delete the used fields in the input/output vector
88 49242 42550.0 0.9 3.8 for psi in self.Lambda_out:
89 22943 28862.0 1.3 2.6 psi.reset_coeff()
90 41015 39436.0 1.0 3.5 for psi in self.Lambda_in:
91 14716 16647.0 1.1 1.5 psi.reset_coeff()
Figure 1 from the document.
Right now we have some double tree living in the spacetime code. At some point you want to invoke the applicator on space/time, which is probably invoked on a fiber/projection. This was/is implemented as a FrozenDoubleNode. So we need a way to make this compatible with the NodeView object. Few options that come to mind:
We need a way to properly deal with hierarchicalbasisfunctions.
It is possible to have multiple roots in our trees. This should be handled by the code somehow.
Try a smaller
Right now, some tests are marked as slow
even though we have the means to speed them up quite a bit.
Most of our constructions are actually trees. We should create a unified OOP setting to incorporate these details.
This allows us to remove the weird "vectorizing" function in various parts of the code.
To implement the multilevel operator in space efficiently, we somehow must design our algorithm to store the matrix operations in the vertex-tree.
The triangulation object is now intertwined with the Element object code. This could be seperated, i.e., we could get rid of the Triangulation object and do everything locally in the Element class.
We know that for this basis, the mass matrix
$$ M := \langle \Psi_\Lambda, \Psi_\Lambda \rangle =: \begin{bmatrix} \int \psi_\lambda \psi_\mu dt \end{bmatrix}_{\lambda,\mu \in \Lambda} $$
should be the identity matrix for any
If we take applicator_test.py:test_orthonormal_multiscale_mass
). However, taking
$$ \Lambda := { (0,0), (0,1), (1,0), (1,1), (2,0), (2,1) } $$
the results are wrong :'(
We need a way to evaluate operators on space trees. Not entirely sure what the correct format is.
Currently, there is a lot of duplicate code in the classes NodeView and DoubleTree. This is because both classes define views
on underlying trees.
A way to unify this would be to introduce a MultiTreeView, a view object that represents a multitree with multiple axes. Then DoubleTree would be an instance with 2 axes, whereas NodeView would be an instance with 1 axis.
Not sure if it's worth the trouble of implementing this, but it seems like it would shorten code.
We should also have a way to store vectors on the spacetime doubletrees. Not sure if this requires a DoubleNodeView, or that we can simply add a coefficient field to DoubleNode...
This will save some memory for the heat_dd_dd_
member of AdaptiveHeatEquation.
Extend the NodeView class to hold a vector coefficient. This will allow to define vectors on (sub)trees.
While at it, you can implement a plus operator. This will be useful.
In the current implementation of applicator_inplace vectors are stored directly on the wavelet/scaling trees. We need to figure out a way to get around this (NodeVector (?)), or carefully clean up these coefficients after using them.
The problem is that we have coefficients op 4 different trees: scaling / wavelet for basis_in and scaling / wavelet for basis_out. In a simple nodevector these couplings are lost..
We need to implement operators, e.g. mass, for different basis_in and basis_out types.
The setting is as follows: we have the orthonormal discontinuous piecewise linear wavelet basis from figure 2. We want to apply the damping matrix
One can verify numerically (see Mathematica/evalA.nb in the git) that the singlescale damping matrix at level 0 equals $C^{ss}_0 = \begin{bmatrix} 0 & 2 \sqrt{3} \\ 0 & 0 \end{bmatrix}$.
Moreover, one can verify using Mathematica (see Mathematica/evalA.nb in the git) that for
If we compute Applicator::apply_upp + Applicator::apply_low
), we retrieve this matrix. However, if we compute it as Applicator::apply
), we get the incorrect matrix $\begin{bmatrix} 0 & 2 * 2 \sqrt{3} & -6 & 0 \\ 0 & 0 & 0 & 6 \\ 0 & 0 & 0 & 2 \sqrt{3} \\0 & 0 & 0 & 0 \end{bmatrix}$.
For higher levels, python/applicator_test.py:test_orthonormal_multiscale_damping_equivalent
to see.
In all of our code, a refine step creates all possible children of the current node, except in the orthonormal basis, where a red node creates just red children, and a black node just black children.
This is ok but not really consistent.
See also the next issue.
In the paper entitled "Fast evaluation of system matrix wrt multi-tree collections of tensor product refinable basis functions", section 2.2 shows evalA. We have evalA in section 3.1 of "Bla Parabolic".
In Bla Parabolic, we define a collection
I originally implemented their version, which introduced problems for the 3-point hierarchical basis on nonuniform grids.
Moreover, no equivalent
In Figure 2 from the followup document, a wavelet basis is depicted. Left part shows the singlescale functions, right part the wavelets. The support of the wavelet functions is of size
For every
For
Our hypothesis is that Python objects are quite slow, and we are making a lot of them constantly (tree nodes, views on trees, etc). It would be nice to experiment with a C++ implementation of, let's say, these tree objects (basically the majority of the datastructures/ directory) which we could then load into Python to use in applicators.
It seems reasonable to try this out with, say, in C++ the time/space tree and a view on this tree, and in Python the corresponding applicator.
As for this C++/Python interface, good candidates seem to by Cppyy and pybind11.
It would be very cool to test the spacetime applicator against quadrature. Not sure how this will work though.
Right now the appliactor code is written in a stateless way, this is annoying for a few reasons:
We should redesign the applicator/operators to allow for more stateful objects.
Create a method that copies an entire nodeview tree. Not sure if really necessary, but might come in handy.
Currently, the operator \gamma_0' \gamma_0 is implemented using a spacetime bilinear form, which means that its implementation requires sigma/theta. I think we could avoid this by creating a custom implementation of the \gamma_0' \gamma_0, or by creating way smaller versions of sigma\theta.
Not sure if its worth the engineering effort.
We should implement the space vertex tree.
This removes one B()->Apply() from the AdaptiveHeatEquation::RHS() code.
To make our spacetime bilinearform multithreading friendly, there is a problem. The current code interacts with the underlying trees at some to evaluate the time applicator, or create the TriangulationView.
A possible solution would be to replicate these fields across all the threads. If I'm not mistaken, the only two fields that need to be replicated are the bool marked
and void *data
insidedatastrucutres/tree.hpp
. These can be replicated, and then the corresponding functions have to be changed to use omp_get_thread_num()
for correct indexing.
Also, on the time side, inside a time applicate, the underlying time trees can grow. That is, prolongation/mass operators refine certain nodes. This is of course also thread unsafe. A possible fix would be adding a mutex to every node, and doing a refine method inside a guarded block.
We need to create a basis for
There is a sort step in two classes: IndexedVector and IntervalSet.
Currently, data gets copied around a lot.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.