From 79af7bcf5df68a1a330769a8a3f26e6c104b5299 Mon Sep 17 00:00:00 2001 From: Daniel Shapero Date: Wed, 1 Jan 2025 17:54:37 -0800 Subject: [PATCH] Explain edge collapse --- demo/simplification.ipynb | 144 +++++++++++++++++++++++++------------- 1 file changed, 97 insertions(+), 47 deletions(-) diff --git a/demo/simplification.ipynb b/demo/simplification.ipynb index 11959fb..78b8aac 100644 --- a/demo/simplification.ipynb +++ b/demo/simplification.ipynb @@ -128,7 +128,9 @@ "metadata": {}, "outputs": [], "source": [ - "triangle_ids = list(set(cotriangles[vertex0]).union(cotriangles[vertex1]))\n", + "triangle_ids0 = cotriangles[vertex0]\n", + "triangle_ids1 = cotriangles[vertex1]\n", + "triangle_ids = list(set(triangle_ids0).union(triangle_ids1))\n", "patch = triangles[triangle_ids]\n", "patch" ] @@ -190,118 +192,166 @@ ] }, { - "cell_type": "code", - "execution_count": null, - "id": "8a107f56-2ff8-4fd6-88ad-7575846fb360", + "cell_type": "markdown", + "id": "e950ce37-00ed-43c4-9cf4-a0432605fa47", "metadata": {}, - "outputs": [], "source": [ - "copatch = [[] for index in range(len(vertex_ids))]\n", - "for index, triangle in enumerate(patch):\n", - " for vertex in triangle:\n", - " copatch[vertex].append(index)\n", - "\n", - "copatch" + "Now we'll convert this simplicial complex into its linear algebraic representation." ] }, { "cell_type": "code", "execution_count": null, - "id": "db782eb7-f6d3-4f80-8398-5ac1d88a4181", + "id": "f22ef651-af3b-455c-8483-f410e07c6d10", "metadata": {}, "outputs": [], "source": [ - "Q0 = zmsh.simplification.compute_qmatrix(points[vertex_ids[patch[copatch[vtx0]]]])\n", - "Q1 = zmsh.simplification.compute_qmatrix(points[vertex_ids[patch[copatch[vtx1]]]])\n", - "Q0, Q1" + "d_0, d_1, d_2 = zmsh.polytopal.from_simplicial(patch)" + ] + }, + { + "cell_type": "markdown", + "id": "87f91fc4-ed34-4c9b-96b0-fd61facb86ce", + "metadata": {}, + "source": [ + "We'll first collapse the edge between vertex 0 and vertex 1.\n", + "To do that, we can perform a row operation on the $d_1$ matrix.\n", + "The operation is to add the rows corresponding to vertex 0 and vertex 1 together.\n", + "We'll then zero out the row corresponding to vertex 1." ] }, { "cell_type": "code", "execution_count": null, - "id": "fe136419-7682-4557-a6b4-ddb1ad062169", + "id": "e451bdec-1d96-4558-aeb5-db06e7e0ddda", "metadata": {}, "outputs": [], "source": [ - "Q = Q0 + Q1\n", - "Q[0, 0] = 1\n", - "Q[0, 1:] = 0\n", - "f = np.array([1, 0, 0, 0])\n", - "r = np.linalg.solve(Q, f)[1:]\n", - "r, points[triangles[0][0]]" + "P = np.eye(len(vertex_ids), dtype=np.int8)\n", + "P[vtx0, [vtx0, vtx1]] = (+1, +1)\n", + "P[vtx1, :] = 0\n", + "P" + ] + }, + { + "cell_type": "markdown", + "id": "b3c894c1-0895-404b-8e8a-172429882dea", + "metadata": {}, + "source": [ + "The cell below shows the resulting matrix, which we'll call $e_1$.\n", + "(I'm showing the transpose so that we don't have to spill lines.)\n", + "You might notice that, for example, rows 0 and 1 are identical.\n", + "Any two rows that are scalar multiples of each other can be merged.\n", + "The adjacency of higher-dimensional cells can then be coalesced into adjacency to the merged cell." ] }, { "cell_type": "code", "execution_count": null, - "id": "f3a012fa-56e3-4f03-ae26-2defa19ad949", + "id": "bc0e2b8c-b2fe-466d-a90c-3c87c2f73139", "metadata": {}, "outputs": [], "source": [ - "fig, ax = plt.subplots(subplot_kw={\"projection\": \"3d\"})\n", - "colors = [\"tab:green\" for index in range(len(vertex_ids))]\n", - "colors[vtx0] = \"tab:orange\"\n", - "colors[vtx1] = \"tab:orange\"\n", - "ax.plot_trisurf(x[vertex_ids], y[vertex_ids], z[vertex_ids], triangles=patch)\n", - "ax.scatter(x[vertex_ids], y[vertex_ids], z[vertex_ids], color=colors)\n", - "ax.scatter([r[0]], [r[1]], [r[2]], color=\"black\");" + "e_1 = P @ d_1\n", + "e_1.T" + ] + }, + { + "cell_type": "markdown", + "id": "2ca5ef8d-739d-4440-a4ea-17db3ff03725", + "metadata": {}, + "source": [ + "The function below creates the matrices that we will multiply by $e_1$ and $d_2$ in order to merge these cells." ] }, { "cell_type": "code", "execution_count": null, - "id": "f22ef651-af3b-455c-8483-f410e07c6d10", + "id": "14b1458c-39c8-449a-bbc1-1323f6d0fc85", "metadata": {}, "outputs": [], "source": [ - "d0, d1, d2 = zmsh.polytopal.from_simplicial(patch)" + "A, B = zmsh.polytopal.make_reduction_matrices(e_1)" + ] + }, + { + "cell_type": "markdown", + "id": "35e83e9d-daa8-49dc-b7e3-134ef16af068", + "metadata": {}, + "source": [ + "The matrix $A$ selects some of the columns of $e_1$." ] }, { "cell_type": "code", "execution_count": null, - "id": "e451bdec-1d96-4558-aeb5-db06e7e0ddda", + "id": "b1696315-05a0-4658-a418-70c747853dc9", "metadata": {}, "outputs": [], "source": [ - "P0 = np.eye(len(vertex_ids), dtype=np.int8)\n", - "P0[vtx0, [vtx0, vtx1]] = (+1, +1)\n", - "P0[vtx1, :] = 0\n", - "P0" + "print(A)" + ] + }, + { + "cell_type": "markdown", + "id": "2130b3cd-f322-4205-b9aa-f95cad2eabb6", + "metadata": {}, + "source": [ + "Meanwhile the matrix $B$ does a row operation to collapse the incidence of any 2-cells to redundant edges down to the remaining non-redundant edges." ] }, { "cell_type": "code", "execution_count": null, - "id": "bc0e2b8c-b2fe-466d-a90c-3c87c2f73139", + "id": "64c97f9e-b118-469e-bb3b-c3f105d7fbdc", "metadata": {}, "outputs": [], "source": [ - "e1 = P0 @ d1\n", - "e1.T" + "print(B)" + ] + }, + { + "cell_type": "markdown", + "id": "fec2c6b5-fd1c-43a5-aa99-877379a6edac", + "metadata": {}, + "source": [ + "If we look at $e_1\\cdot A$, we can see that there are no columns left that are multiples of each other." ] }, { "cell_type": "code", "execution_count": null, - "id": "67f584cf-f121-47b6-8fa7-35b45710e4c9", + "id": "1088710f-4b44-49be-bf17-ac2c5df9b8ac", "metadata": {}, "outputs": [], "source": [ - "for index0, col0 in enumerate(e1.T):\n", - " for index1, col1 in enumerate(e1.T):\n", - " if (np.array_equal(col0, col1) or np.array_equal(col0, -col1)) and (index0 != index1) and (not np.all(col0 == 0)):\n", - " print(index0, index1)" + "print(e_1 @ A)" + ] + }, + { + "cell_type": "markdown", + "id": "1e546417-668c-4420-836b-7d3a640de149", + "metadata": {}, + "source": [ + "And the columns of $B\\cdot d_2$ have their adjacencies to redundant columns summed together." ] }, { "cell_type": "code", "execution_count": null, - "id": "42db5da8-937c-418f-8112-02cbc4a49918", + "id": "2f5b82f2-eb7e-4e36-bfbd-49e8005ba144", "metadata": {}, "outputs": [], "source": [ - "zmsh.polytopal.merge([e1, d2], face_ids=[3, 13])" + "print(B @ d_2)" + ] + }, + { + "cell_type": "markdown", + "id": "71ba922a-096d-4108-b205-bec49df309d2", + "metadata": {}, + "source": [ + "You'll also note that some 2-cells are now empty, which we expect." ] } ],