A differentiable JAX-based framework for vertex modeling and inverse design of epithelial tissues.\n\nEpithelial tissues dynamically reshape through local mechanical interactions among cells.\nUnderstanding, inferring, and designing these mechanics is a central challenge in developmental biology and biophysics.\nVertAX is a computational framework built to address this challenge.
\n\nVertAX is a framework for vertex-based modeling :\n it represents epithelial tissues as two-dimensional polygonal meshes in which cells are faces, junctions are edges,\n tricellular contacts are vertices, and mechanical equilibrium is defined by the minimum of a user-specified energy.
\n\nBuilt on JAX , VertAX is designed not only for forward simulation,\n but also for inverse problems such as parameter inference and tissue design.
\n\nIn VertAX , a Mesh represents a vertex model of an epithelial tissue. It is made of faces (cells), edges (interface between cells) and vertices (where 3 cells or more meet).
\n\nMore specifically, two types of meshes are currently supported:
\n\n\nPbcMesh have Periodic Boundary Conditions, and are used for bulk tissue dynamics without explicit external boundaries. \nBoundedMesh are designed for finite tissue clusters, with curved free interfaces. \n \n\nThe other first class objects of VertAX are called Bilevel Optimizers. They allow to formulate inverse problems as nested optimizations:
\n\n$$\n\\begin{aligned}\n\\textbf{Outer problem (learning):} \\quad\n\\theta^{\\ast} &= \\arg\\min_{\\theta} \\mathcal{C}\\left(X^{\\ast}_{\\theta},\\theta\\right)\n&& \\leftarrow \\text{fit data or reach a target} \\\n\\end{aligned}\n$$\n$$\n\\begin{aligned}\n\\textbf{Inner problem (physics):} \\quad \\text{s.t.}&\nX^{\\ast}_{\\theta} \\in \\arg\\min_{X} \\mathcal{E}(X,\\theta)\n&& \\leftarrow \\text{compute mechanical equilibrium}\n\\end{aligned}\n$$
\n\nHere, $X$ denotes the tissue configuration, i.e. the vertex positions of the mesh, and $\\theta$ denotes the model parameters, such as line tensions, target areas, or shape factors.
\n\nIn other words, VertAX repeatedly solves a mechanical equilibrium problem for a given parameter set $\\theta$, then updates those parameters to better match data or a design objective.
\n\nIn symmetry with meshes, a base abstract class _BilevelOptimizer defines common hyper-parameters and methods for the bilevel optimization, but you need to use the specialized classes:
\n\n\nPbcBilevelOptimizer for PbcMesh, \nBoundedBilevelOptimizer for BoundedMesh. \n \n\nVertAX comes with pre-defined energy and cost functions but you can easily define your own functions. See the examples folder in the repository to have a typical example on how to use VertAX .
\n\nFinally, there are plot functions to easily see the results of your experiments. See plot_mesh for example.
\n\nUsers can define their own energy functions for the inner optimization and cost function for the outer optimization, however we provide some basic ones.\nIf you use your own make sure to use the exact same signature as we do for these functions, otherwise it won't work. See the cost and energy functions we provide in this documentation.
\n"}, "vertax.Mesh": {"fullname": "vertax.Mesh", "modulename": "vertax", "qualname": "Mesh", "kind": "class", "doc": "Generic mesh structure. It is an abstract base class, not to be used directly.
\n\nIt defines common attributes and functions between PbcMesh and BoundedMesh.
\n"}, "vertax.Mesh.__init__": {"fullname": "vertax.Mesh.__init__", "modulename": "vertax", "qualname": "Mesh.__init__", "kind": "function", "doc": "Do nothing but create attributes. Do not call this, but call specialized class methods to create meshes.
\n\nSee PbcMesh and BoundedMesh.
\n\nTechnically, it uses a DCEL structure.
\n", "signature": "() "}, "vertax.Mesh.faces": {"fullname": "vertax.Mesh.faces", "modulename": "vertax", "qualname": "Mesh.faces", "kind": "variable", "doc": "The cells of the tissue.
\n", "annotation": ": jax.Array"}, "vertax.Mesh.edges": {"fullname": "vertax.Mesh.edges", "modulename": "vertax", "qualname": "Mesh.edges", "kind": "variable", "doc": "The interface between cells. Technically half-edges.
\n", "annotation": ": jax.Array"}, "vertax.Mesh.vertices": {"fullname": "vertax.Mesh.vertices", "modulename": "vertax", "qualname": "Mesh.vertices", "kind": "variable", "doc": "The mesh vertices, where cells meet.
\n", "annotation": ": jax.Array"}, "vertax.Mesh.width": {"fullname": "vertax.Mesh.width", "modulename": "vertax", "qualname": "Mesh.width", "kind": "variable", "doc": "The mesh live in a rectangle of size [0, width] in the X direction.
\n", "annotation": ": float"}, "vertax.Mesh.height": {"fullname": "vertax.Mesh.height", "modulename": "vertax", "qualname": "Mesh.height", "kind": "variable", "doc": "The mesh live in a rectangle of size [0, height] in the Y direction.
\n", "annotation": ": float"}, "vertax.Mesh.faces_params": {"fullname": "vertax.Mesh.faces_params", "modulename": "vertax", "qualname": "Mesh.faces_params", "kind": "variable", "doc": "Parameters attached to faces. Can be optimized.
\n", "annotation": ": jax.Array"}, "vertax.Mesh.edges_params": {"fullname": "vertax.Mesh.edges_params", "modulename": "vertax", "qualname": "Mesh.edges_params", "kind": "variable", "doc": "Parameters attached to edges. Can be optimized.
\n", "annotation": ": jax.Array"}, "vertax.Mesh.vertices_params": {"fullname": "vertax.Mesh.vertices_params", "modulename": "vertax", "qualname": "Mesh.vertices_params", "kind": "variable", "doc": "Parameters attached to vertices. Can be optimized.
\n", "annotation": ": jax.Array"}, "vertax.Mesh.nb_faces": {"fullname": "vertax.Mesh.nb_faces", "modulename": "vertax", "qualname": "Mesh.nb_faces", "kind": "variable", "doc": "Get the number of faces of the mesh.
\n", "annotation": ": int"}, "vertax.Mesh.nb_edges": {"fullname": "vertax.Mesh.nb_edges", "modulename": "vertax", "qualname": "Mesh.nb_edges", "kind": "variable", "doc": "Get the number of edges of the mesh.
\n", "annotation": ": int"}, "vertax.Mesh.nb_half_edges": {"fullname": "vertax.Mesh.nb_half_edges", "modulename": "vertax", "qualname": "Mesh.nb_half_edges", "kind": "variable", "doc": "Get the number of half-edges of the mesh, ie. twice the number of edges.
\n", "annotation": ": int"}, "vertax.Mesh.nb_vertices": {"fullname": "vertax.Mesh.nb_vertices", "modulename": "vertax", "qualname": "Mesh.nb_vertices", "kind": "variable", "doc": "Get the number of vertices of the mesh.
\n", "annotation": ": int"}, "vertax.Mesh.save_mesh": {"fullname": "vertax.Mesh.save_mesh", "modulename": "vertax", "qualname": "Mesh.save_mesh", "kind": "function", "doc": "Save mesh to a file.
\n\nAll mesh data is saved.
\n\nArguments: \n\n\npath (str): Path to the saved file. The extension is .npz. \n \n", "signature": "(self , path : str ) -> None : ", "funcdef": "def"}, "vertax.Mesh.load_mesh": {"fullname": "vertax.Mesh.load_mesh", "modulename": "vertax", "qualname": "Mesh.load_mesh", "kind": "function", "doc": "Load a mesh from a file.
\n\nArguments: \n\n\npath (str): Path to the mesh file (.npz). \n \n\nReturns: \n\n\n The mesh loaded from the .npz file.
\n \n", "signature": "(cls , path : str ) -> Self : ", "funcdef": "def"}, "vertax.PbcMesh": {"fullname": "vertax.PbcMesh", "modulename": "vertax", "qualname": "PbcMesh", "kind": "class", "doc": "Periodic Boundary Condition on a mesh.
\n\nFor a PbcMesh, vertices is a 2D array of floats of size (nb_vertices, 2) ;\nwith the coordinates of the vertices (in ]0, width[ x ]0, height[ ).
\n\nedges is a 2D array of integers of size (nb_half_edges, 8), with:
\n\n\nid of previous half-edge, \nid of next half-edge, \nid of twin half-edge, \nid of source vertex, \nid of target vertex, \nid of the face containing the half-edge. \nx-offset (either -1, 0 or 1) depending on the target vertex crossing a boundary. \ny-offset (either -1, 0 or 1) depending on the target vertex crossing a boundary. \n \n\nfaces is a 1D array of integers of size (nb_faces) containing the id of a half-edge belonging to this face.
\n", "bases": "vertax.meshes.mesh.Mesh"}, "vertax.PbcMesh.__init__": {"fullname": "vertax.PbcMesh.__init__", "modulename": "vertax", "qualname": "PbcMesh.__init__", "kind": "function", "doc": "Do not call the constructor directly, use the dedicated class methods such as:
\n\n\nfrom_random_seeds, \nfrom_seeds, \nfrom_image, \nfrom_mask, \ncreate_empty. \n \n", "signature": "() "}, "vertax.PbcMesh.MAX_EDGES_IN_ANY_FACE": {"fullname": "vertax.PbcMesh.MAX_EDGES_IN_ANY_FACE", "modulename": "vertax", "qualname": "PbcMesh.MAX_EDGES_IN_ANY_FACE", "kind": "variable", "doc": "Optimization parameter : must be more than the estimated maximum number of edges in a face. Base value is 20.
\n", "annotation": ": int"}, "vertax.PbcMesh.from_random_seeds": {"fullname": "vertax.PbcMesh.from_random_seeds", "modulename": "vertax", "qualname": "PbcMesh.from_random_seeds", "kind": "function", "doc": "Create a Periodic Boundary Conditions Mesh from a Voronoi diagram based on random seeds.
\n\nArguments: \n\n\nnb_seeds (int): Number of random seeds to use. \nwidth (float): Width of the rectangular domains the seeds will be in. \nheight (float): Height of the rectangular domains the seeds will be in. \nrandom_key (int): Set the random key for reproducibility. \n \n\nReturns: \n\n\n Self: The corresponding mesh.
\n \n", "signature": "(cls , nb_seeds : int , width : float , height : float , random_key : int ) -> Self : ", "funcdef": "def"}, "vertax.PbcMesh.from_seeds": {"fullname": "vertax.PbcMesh.from_seeds", "modulename": "vertax", "qualname": "PbcMesh.from_seeds", "kind": "function", "doc": "Create a Periodic Boundary Conditions Mesh from a Voronoi diagram based on a list of seeds.
\n\nThe seeds are assumed to have positive x and y positions.
\n\nArguments: \n\n\nseeds (Array[float32]): jax float array of seed positions of shape (nbSeeds, 2). \nwidth (float): width of the box containing the seeds. \nheight (float): height of the box containing the seeds. \n \n", "signature": "(cls , seeds : jax . Array , width : float , height : float ) -> Self : ", "funcdef": "def"}, "vertax.PbcMesh.from_image": {"fullname": "vertax.PbcMesh.from_image", "modulename": "vertax", "qualname": "PbcMesh.from_image", "kind": "function", "doc": "Create a rudimentary mesh with periodic boundary conditions from an image.
\n\nTo do that, we perform a segmentation using Cellpose and we try to fill the holes.\nThe result will probably be imperfect and it will always be better if you\nprovide directly a mask (with no holes) with the function \"periodic_from_mask\".
\n\nThe mask is made periodic by mirroring its edges.
\n\nArguments: \n\n\nimage (NDArray): The image which will act as a template for the mesh. \n \n\nReturns: \n\n\n Self: The corresponding mesh.
\n \n", "signature": "(\tcls , \timage : numpy . ndarray [ typing . Any , numpy . dtype [ + _ScalarType_co ]] ) -> Self : ", "funcdef": "def"}, "vertax.PbcMesh.from_mask": {"fullname": "vertax.PbcMesh.from_mask", "modulename": "vertax", "qualname": "PbcMesh.from_mask", "kind": "function", "doc": "Create a rudimentary mesh with periodic boundary conditions from a mask with no holes.
\n\nThe mask is made periodic by mirroring its edges.
\n\nArguments: \n\n\nmask (NDArray): The mask with no holes which will act as a template for the mesh. \n \n\nReturns: \n\n\n Self: The corresponding mesh.
\n \n", "signature": "(\tcls , \tmask : numpy . ndarray [ typing . Any , numpy . dtype [ + _ScalarType_co ]] ) -> Self : ", "funcdef": "def"}, "vertax.PbcMesh.create_empty": {"fullname": "vertax.PbcMesh.create_empty", "modulename": "vertax", "qualname": "PbcMesh.create_empty", "kind": "function", "doc": "Create an empty mesh. Use if you know what you're doing !
\n", "signature": "(cls ) -> Self : ", "funcdef": "def"}, "vertax.PbcMesh.copy_mesh": {"fullname": "vertax.PbcMesh.copy_mesh", "modulename": "vertax", "qualname": "PbcMesh.copy_mesh", "kind": "function", "doc": "Copy all parameters from another mesh in a new mesh.
\n", "signature": "(cls , other_mesh : Self ) -> Self : ", "funcdef": "def"}, "vertax.PbcMesh.save_mesh": {"fullname": "vertax.PbcMesh.save_mesh", "modulename": "vertax", "qualname": "PbcMesh.save_mesh", "kind": "function", "doc": "Save mesh to a file.
\n\nAll PBCMesh data is saved.
\n\nArguments: \n\n\npath (str): Path to the saved file. The extension is .npz, a numpy format. \n \n", "signature": "(self , path : str ) -> None : ", "funcdef": "def"}, "vertax.PbcMesh.load_mesh": {"fullname": "vertax.PbcMesh.load_mesh", "modulename": "vertax", "qualname": "PbcMesh.load_mesh", "kind": "function", "doc": "Load a mesh from a file.
\n\nAll PBCMesh data is reloaded.
\n\nArguments: \n\n\npath (str): Path to the mesh file (.npz), numpy format. \n \n\nReturns: \n\n\n Mesh: the mesh loaded from the numpy .npz file.
\n \n", "signature": "(cls , path : str ) -> Self : ", "funcdef": "def"}, "vertax.PbcMesh.save_mesh_txt": {"fullname": "vertax.PbcMesh.save_mesh_txt", "modulename": "vertax", "qualname": "PbcMesh.save_mesh_txt", "kind": "function", "doc": "Save a mesh in separate text files that can be read by numpy.
\n\nOnly save the vertices, edges and faces, not other parameters.
\n\nArguments: \n\n\ndirectory (str): Path to the directory where to save the files. \nvertices_filename (str, optional): Filename for the vertices table. Defaults to \"vertTable.txt\". \nedges_filename (str, optional): Filename for the half-edges table. Defaults to \"heTable.txt\". \nfaces_filename (str, optional): Filename for the faces table. Defaults to \"faceTable.txt\". \nvertices_params_filename (str, optional): Filename for the vertices parameters table.\nDefaults to \"vertParamsTable.txt\". \nedges_params_filename (str, optional): Filename for the half-edges parameters table.\nDefaults to \"heParamsTable.txt\". \nfaces_params_filename (str, optional): Filename for the faces parameters table.\nDefaults to \"faceParamsTable.txt\". \nconstants_filename (str, optional): Filename for width/height/MAX_EDGES_IN_ANY_FACE.\nDefaults to \"constants.txt\". \n \n", "signature": "(\tself , \tdirectory : str , \tvertices_filename : str = 'vertTable.txt' , \tedges_filename : str = 'heTable.txt' , \tfaces_filename : str = 'faceTable.txt' , \tvertices_params_filename : str = 'vertParamsTable.txt' , \tedges_params_filename : str = 'heParamsTable.txt' , \tfaces_params_filename : str = 'faceParamsTable.txt' , \tconstants_filename : str = 'constants.txt' ) -> None : ", "funcdef": "def"}, "vertax.PbcMesh.load_mesh_txt": {"fullname": "vertax.PbcMesh.load_mesh_txt", "modulename": "vertax", "qualname": "PbcMesh.load_mesh_txt", "kind": "function", "doc": "Load a mesh from text files.
\n\nOnly load the vertices, edges and faces, not other parameters.
\n\nArguments: \n\n\ndirectory (str): Directory where the text files are stored. \nvertices_filename (str, optional): Filename for the vertices table. Defaults to \"vertTable.txt\". \nedges_filename (str, optional): Filename for the half-edges table. Defaults to \"heTable.txt\". \nfaces_filename (str, optional): Filename for the faces table. Defaults to \"faceTable.txt\". \nvertices_params_filename (str, optional): Filename for the vertices parameters table.\nDefaults to \"vertParamsTable.txt\". \nedges_params_filename (str, optional): Filename for the half-edges parameters table.\nDefaults to \"heParamsTable.txt\". \nfaces_params_filename (str, optional): Filename for the faces parameters table.\nDefaults to \"faceParamsTable.txt\". \nconstants_filename (str, optional): Filename for width/height/MAX_EDGES_IN_ANY_FACE.\nDefaults to \"constants.txt\". \n \n\nReturns: \n\n\n Self: The loaded mesh.
\n \n", "signature": "(\tcls , \tdirectory : str , \tvertices_filename : str = 'vertTable.txt' , \tedges_filename : str = 'heTable.txt' , \tfaces_filename : str = 'faceTable.txt' , \tvertices_params_filename : str = 'vertParamsTable.txt' , \tedges_params_filename : str = 'heParamsTable.txt' , \tfaces_params_filename : str = 'faceParamsTable.txt' , \tconstants_filename : str = 'constants.txt' ) -> Self : ", "funcdef": "def"}, "vertax.PbcMesh.get_length": {"fullname": "vertax.PbcMesh.get_length", "modulename": "vertax", "qualname": "PbcMesh.get_length", "kind": "function", "doc": "Get the length of given edges.
\n", "signature": "(self , half_edge_id : jax . Array ) -> jax . Array : ", "funcdef": "def"}, "vertax.PbcMesh.get_length_with_offset": {"fullname": "vertax.PbcMesh.get_length_with_offset", "modulename": "vertax", "qualname": "PbcMesh.get_length_with_offset", "kind": "function", "doc": "Get the length of given edges along with its offsets in an array (length, offset x, offset y).
\n", "signature": "(self , half_edge_id : jax . Array ) -> jax . Array : ", "funcdef": "def"}, "vertax.PbcMesh.get_perimeter": {"fullname": "vertax.PbcMesh.get_perimeter", "modulename": "vertax", "qualname": "PbcMesh.get_perimeter", "kind": "function", "doc": "Get the perimeter of given faces.
\n", "signature": "(self , face_id : jax . Array ) -> jax . Array : ", "funcdef": "def"}, "vertax.PbcMesh.get_area": {"fullname": "vertax.PbcMesh.get_area", "modulename": "vertax", "qualname": "PbcMesh.get_area", "kind": "function", "doc": "Get the area of given faces.
\n", "signature": "(self , face_id : jax . Array ) -> jax . Array : ", "funcdef": "def"}, "vertax.PbcMesh.update_boundary_conditions": {"fullname": "vertax.PbcMesh.update_boundary_conditions", "modulename": "vertax", "qualname": "PbcMesh.update_boundary_conditions", "kind": "function", "doc": "Force periodic boundary conditions again after an update.
\n\nMost likely you'll never have to use it yourself, as it can be made automatically with a PbcBilevelOptimizer.
\n", "signature": "(self ) -> None : ", "funcdef": "def"}, "vertax.BoundedMesh": {"fullname": "vertax.BoundedMesh", "modulename": "vertax", "qualname": "BoundedMesh", "kind": "class", "doc": "Bounded mesh with arc circles for boundary cells.
\n\nFor a BoundedMesh, vertices is a 2D array of floats of size (nb_vertices, 2) ;\nwith the coordinates of the vertices (in ]0, width[ x ]0, height[ ).
\n\nedges is a 2D array of integers of size (nb_half_edges, 8), with:
\n\n\nid of previous half-edge, \nid of next half-edge, \nid of twin half-edge, \nid of source vertex + 2 if current half-edge is an inside edge, else 0, \nid of target vertex + 2 if current half-edge is an inside edge, else 1, \nid of source vertex + 2 if current half-edge is an outside edge, else 0, \nid of target vertex + 2 if current half-edge is an outside edge, else 1, \nid of the face containing the half-edge. \n \n\nfaces is a 1D array of integers of size (nb_faces) containing the id of a half-edge belonging to this face.
\n\nangles is a 1D array of floats of size (nb_angles), with the angles sustaining the arcs of the free interfaces ;\nbetween 0 and PI / 2.
\n", "bases": "vertax.meshes.mesh.Mesh"}, "vertax.BoundedMesh.__init__": {"fullname": "vertax.BoundedMesh.__init__", "modulename": "vertax", "qualname": "BoundedMesh.__init__", "kind": "function", "doc": "Do not call the constructor.
\n", "signature": "() "}, "vertax.BoundedMesh.angles": {"fullname": "vertax.BoundedMesh.angles", "modulename": "vertax", "qualname": "BoundedMesh.angles", "kind": "variable", "doc": "Angle sustaining the arced free interfaces. Between 0 and PI / 2.
\n", "annotation": ": jax.Array"}, "vertax.BoundedMesh.nb_angles": {"fullname": "vertax.BoundedMesh.nb_angles", "modulename": "vertax", "qualname": "BoundedMesh.nb_angles", "kind": "variable", "doc": "Get the number of angles (free interfaces) of the mesh.
\n", "annotation": ": int"}, "vertax.BoundedMesh.from_random_seeds": {"fullname": "vertax.BoundedMesh.from_random_seeds", "modulename": "vertax", "qualname": "BoundedMesh.from_random_seeds", "kind": "function", "doc": "Create a bounded Mesh from random seeds, based on a Voronoi diagram with arced free interfaces.
\n\nArguments: \n\n\nnb_seeds (int): Number of random seeds to use. \nwidth (float): width of the box containing the seeds. \nheight (float): height of the box containing the seeds. \nrandom_key (int): seed for a random number generator to add new seeds if needed, decide on cell fates... \nnb_fates (int, default=2): number of possible different fate marker for a cell. \n \n\nReturns: \n\n\n Self: The corresponding mesh.
\n \n", "signature": "(\tcls , \tnb_seeds : int , \twidth : float , \theight : float , \trandom_key : int , \tnb_fates : int = 2 ) -> Self : ", "funcdef": "def"}, "vertax.BoundedMesh.from_seeds": {"fullname": "vertax.BoundedMesh.from_seeds", "modulename": "vertax", "qualname": "BoundedMesh.from_seeds", "kind": "function", "doc": "Create a bounded Mesh from a list of given seeds.
\n\nThe seeds are assumed to have x-coordinate in ]0, width[ and y-coordinate in ]0, height[.\nNote that the final mesh might not use your seeds if they don't work to create a correct\nbounded mesh via our method.
\n\nArguments: \n\n\nseeds (Array[float32]): jax float array of seed positions of shape (nbSeeds, 2). \nwidth (float): width of the box containing the seeds. \nheight (float): height of the box containing the seeds. \nrandom_key (int): seed for a random number generator to add new seeds if needed, decide on cell fates... \nnb_fates (int, default=2): number of possible different fate marker for a cell. \n \n", "signature": "(\tcls , \tseeds : numpy . ndarray [ typing . Any , numpy . dtype [ + _ScalarType_co ]] , \twidth : float , \theight : float , \trandom_key : int , \tnb_fates : int = 2 ) -> Self : ", "funcdef": "def"}, "vertax.BoundedMesh.create_empty": {"fullname": "vertax.BoundedMesh.create_empty", "modulename": "vertax", "qualname": "BoundedMesh.create_empty", "kind": "function", "doc": "Create an empty mesh. Use if you know what you're doing !
\n", "signature": "(cls ) -> Self : ", "funcdef": "def"}, "vertax.BoundedMesh.copy_mesh": {"fullname": "vertax.BoundedMesh.copy_mesh", "modulename": "vertax", "qualname": "BoundedMesh.copy_mesh", "kind": "function", "doc": "Copy all parameters from another mesh in a new mesh.
\n", "signature": "(cls , other_mesh : Self ) -> Self : ", "funcdef": "def"}, "vertax.BoundedMesh.save_mesh": {"fullname": "vertax.BoundedMesh.save_mesh", "modulename": "vertax", "qualname": "BoundedMesh.save_mesh", "kind": "function", "doc": "Save mesh to a file.
\n\nAll BoundedMesh data is saved.
\n\nArguments: \n\n\npath (str): Path to the saved file. The extension is .npz. \n \n", "signature": "(self , path : str ) -> None : ", "funcdef": "def"}, "vertax.BoundedMesh.load_mesh": {"fullname": "vertax.BoundedMesh.load_mesh", "modulename": "vertax", "qualname": "BoundedMesh.load_mesh", "kind": "function", "doc": "Load a mesh from a file.
\n\nAll BoundedMesh data is reloaded.
\n\nArguments: \n\n\npath (str): Path to the mesh file (.npz). \n \n\nReturns: \n\n\n Mesh: the mesh loaded from the .npz file.
\n \n", "signature": "(cls , path : str ) -> Self : ", "funcdef": "def"}, "vertax.BoundedMesh.save_mesh_txt": {"fullname": "vertax.BoundedMesh.save_mesh_txt", "modulename": "vertax", "qualname": "BoundedMesh.save_mesh_txt", "kind": "function", "doc": "Save a mesh in separate text files that can be read by numpy.
\n\nOnly save the vertices, angles, edges and faces, not other parameters.
\n\nArguments: \n\n\ndirectory (str): Path to the directory where to save the files. \nvertices_filename (str, optional): Filename for the vertices table. Defaults to \"vertTable.txt\". \nangles_filename (str, optional): Filename for the angles table. Defaults to \"angTable.txt\". \nedges_filename (str, optional): Filename for the half-edges table. Defaults to \"heTable.txt\". \nfaces_filename (str, optional): Filename for the faces table. Defaults to \"faceTable.txt\". \nvertices_params_filename (str, optional): Filename for the vertices parameters table.\nDefaults to \"vertParamsTable.txt\". \nedges_params_filename (str, optional): Filename for the half-edges parameters table.\nDefaults to \"heParamsTable.txt\". \nfaces_params_filename (str, optional): Filename for the faces parameters table.\nDefaults to \"faceParamsTable.txt\". \nconstants_filename (str, optional): Filename for width/height.\nDefaults to \"constants.txt\". \n \n", "signature": "(\tself , \tdirectory : str , \tvertices_filename : str = 'vertTable.txt' , \tangles_filename : str = 'angTable.txt' , \tedges_filename : str = 'heTable.txt' , \tfaces_filename : str = 'faceTable.txt' , \tvertices_params_filename : str = 'vertParamsTable.txt' , \tedges_params_filename : str = 'heParamsTable.txt' , \tfaces_params_filename : str = 'faceParamsTable.txt' , \tconstants_filename : str = 'constants.txt' ) -> None : ", "funcdef": "def"}, "vertax.BoundedMesh.load_mesh_txt": {"fullname": "vertax.BoundedMesh.load_mesh_txt", "modulename": "vertax", "qualname": "BoundedMesh.load_mesh_txt", "kind": "function", "doc": "Load a mesh from text files.
\n\nOnly load the vertices, angles, edges and faces, not other parameters.
\n\nArguments: \n\n\ndirectory (str): Directory where the text files are stored. \nvertices_filename (str, optional): Filename for the vertices table. Defaults to \"vertTable.txt\". \nangles_filename (str, optional): Filename for the angles table. Defaults to \"angTable.txt\". \nedges_filename (str, optional): Filename for the half-edges table. Defaults to \"heTable.txt\". \nfaces_filename (str, optional): Filename for the faces table. Defaults to \"faceTable.txt\". \nvertices_params_filename (str, optional): Filename for the vertices parameters table.\nDefaults to \"vertParamsTable.txt\". \nedges_params_filename (str, optional): Filename for the half-edges parameters table.\nDefaults to \"heParamsTable.txt\". \nfaces_params_filename (str, optional): Filename for the faces parameters table.\nDefaults to \"faceParamsTable.txt\". \nconstants_filename (str, optional): Filename for width/height/MAX_EDGES_IN_ANY_FACE.\nDefaults to \"constants.txt\". \n \n\nReturns: \n\n\n Self: The loaded mesh.
\n \n", "signature": "(\tcls , \tdirectory : str , \tvertices_filename : str = 'vertTable.txt' , \tangles_filename : str = 'angTable.txt' , \tedges_filename : str = 'heTable.txt' , \tfaces_filename : str = 'faceTable.txt' , \tvertices_params_filename : str = 'vertParamsTable.txt' , \tedges_params_filename : str = 'heParamsTable.txt' , \tfaces_params_filename : str = 'faceParamsTable.txt' , \tconstants_filename : str = 'constants.txt' ) -> Self : ", "funcdef": "def"}, "vertax.BoundedMesh.get_length": {"fullname": "vertax.BoundedMesh.get_length", "modulename": "vertax", "qualname": "BoundedMesh.get_length", "kind": "function", "doc": "Get the length of an edge.
\n", "signature": "(self , half_edge_id : jax . Array ) -> jax . Array : ", "funcdef": "def"}, "vertax.BoundedMesh.get_perimeter": {"fullname": "vertax.BoundedMesh.get_perimeter", "modulename": "vertax", "qualname": "BoundedMesh.get_perimeter", "kind": "function", "doc": "Get the area of a face.
\n", "signature": "(self , face_id : jax . Array ) -> jax . Array : ", "funcdef": "def"}, "vertax.BoundedMesh.get_area": {"fullname": "vertax.BoundedMesh.get_area", "modulename": "vertax", "qualname": "BoundedMesh.get_area", "kind": "function", "doc": "Get the area of a face.
\n", "signature": "(self , face_id : jax . Array ) -> jax . Array : ", "funcdef": "def"}, "vertax.BilevelOptimizationMethod": {"fullname": "vertax.BilevelOptimizationMethod", "modulename": "vertax", "qualname": "BilevelOptimizationMethod", "kind": "class", "doc": "Which optimization method to use in the bi-level optimization.
\n", "bases": "enum.Enum"}, "vertax.BilevelOptimizationMethod.AUTOMATIC_DIFFERENTIATION": {"fullname": "vertax.BilevelOptimizationMethod.AUTOMATIC_DIFFERENTIATION", "modulename": "vertax", "qualname": "BilevelOptimizationMethod.AUTOMATIC_DIFFERENTIATION", "kind": "variable", "doc": "Unrolls the inner optimization steps ; forward-mode JVP via jax.jacfwd,\ncost scales with the number of parameters and iterations.
\n", "default_value": "<BilevelOptimizationMethod.AUTOMATIC_DIFFERENTIATION: 'ad'>"}, "vertax.BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION": {"fullname": "vertax.BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION", "modulename": "vertax", "qualname": "BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION", "kind": "variable", "doc": "Estimates the gradient from perturbed free vs nudged equilibria ; no backdrop required.\nMost efficient but depends on the perturbation size \u03b2.
\n", "default_value": "<BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION: 'ep'>"}, "vertax.BilevelOptimizationMethod.IMPLICIT_DIFFERENTIATION": {"fullname": "vertax.BilevelOptimizationMethod.IMPLICIT_DIFFERENTIATION", "modulename": "vertax", "qualname": "BilevelOptimizationMethod.IMPLICIT_DIFFERENTIATION", "kind": "variable", "doc": "Differentiates the optimality condition \u2207\u2093E=0 via Implicit Function Theorem ; JVP variant ;\nrequires Hessian solve and sensitive to ill-conditioning.
\n", "default_value": "<BilevelOptimizationMethod.IMPLICIT_DIFFERENTIATION: 'id'>"}, "vertax.BilevelOptimizationMethod.ADJOINT_STATE": {"fullname": "vertax.BilevelOptimizationMethod.ADJOINT_STATE", "modulename": "vertax", "qualname": "BilevelOptimizationMethod.ADJOINT_STATE", "kind": "variable", "doc": "Differentiates the optimality condition \u2207\u2093E=0 via Implicit Function Theorem ; VJP variant ;\nrequires Hessian solve or sensitive to ill-conditioning.
\n", "default_value": "<BilevelOptimizationMethod.ADJOINT_STATE: 'as'>"}, "vertax.PbcBilevelOptimizer": {"fullname": "vertax.PbcBilevelOptimizer", "modulename": "vertax", "qualname": "PbcBilevelOptimizer", "kind": "class", "doc": "Bi-level optimizer for periodic boundary condition meshes (PbcMesh).
\n", "bases": "vertax.bilevelopt.bilevelopt._BilevelOptimizer"}, "vertax.PbcBilevelOptimizer.__init__": {"fullname": "vertax.PbcBilevelOptimizer.__init__", "modulename": "vertax", "qualname": "PbcBilevelOptimizer.__init__", "kind": "function", "doc": "Create a Bi-level optimizer for periodic boundary condition meshes with default parameters.
\n\nDoes not specify the inner and outer loss yet.
\n", "signature": "() "}, "vertax.PbcBilevelOptimizer.compute_outer_loss": {"fullname": "vertax.PbcBilevelOptimizer.compute_outer_loss", "modulename": "vertax", "qualname": "PbcBilevelOptimizer.compute_outer_loss", "kind": "function", "doc": "Get the result of self.loss_function_outer called with the correct arguments.
\n", "signature": "(\tself , \tmesh : vertax . meshes . mesh . Mesh , \tonly_on_vertices : None | list [ int ] = None , \tonly_on_edges : None | list [ int ] = None , \tonly_on_faces : None | list [ int ] = None ) -> float : ", "funcdef": "def"}, "vertax.PbcBilevelOptimizer.compute_inner_loss": {"fullname": "vertax.PbcBilevelOptimizer.compute_inner_loss", "modulename": "vertax", "qualname": "PbcBilevelOptimizer.compute_inner_loss", "kind": "function", "doc": "Get the result of self.loss_function_inner called with the correct arguments.
\n", "signature": "(\tself , \tmesh : vertax . meshes . mesh . Mesh , \tonly_on_vertices : None | list [ int ] = None , \tonly_on_edges : None | list [ int ] = None , \tonly_on_faces : None | list [ int ] = None ) -> float : ", "funcdef": "def"}, "vertax.BoundedBilevelOptimizer": {"fullname": "vertax.BoundedBilevelOptimizer", "modulename": "vertax", "qualname": "BoundedBilevelOptimizer", "kind": "class", "doc": "Bi-level optimizer for bounded meshes (BoundedMesh).
\n", "bases": "vertax.bilevelopt.bilevelopt._BilevelOptimizer"}, "vertax.BoundedBilevelOptimizer.__init__": {"fullname": "vertax.BoundedBilevelOptimizer.__init__", "modulename": "vertax", "qualname": "BoundedBilevelOptimizer.__init__", "kind": "function", "doc": "Create a Bi-level optimizer for bounded meshes with default parameters.
\n\nDoes not set the inner and outer loss functions yet.
\n", "signature": "() "}, "vertax.BoundedBilevelOptimizer.angles_target": {"fullname": "vertax.BoundedBilevelOptimizer.angles_target", "modulename": "vertax", "qualname": "BoundedBilevelOptimizer.angles_target", "kind": "variable", "doc": "
\n"}, "vertax.BoundedBilevelOptimizer.compute_outer_loss": {"fullname": "vertax.BoundedBilevelOptimizer.compute_outer_loss", "modulename": "vertax", "qualname": "BoundedBilevelOptimizer.compute_outer_loss", "kind": "function", "doc": "Get the result of self.loss_function_outer called with the correct arguments.
\n\nMust be implemented by child classes.
\n", "signature": "(\tself , \tmesh : vertax . meshes . mesh . Mesh , \tonly_on_vertices : None | list [ int ] = None , \tonly_on_edges : None | list [ int ] = None , \tonly_on_faces : None | list [ int ] = None ) -> float : ", "funcdef": "def"}, "vertax.BoundedBilevelOptimizer.compute_inner_loss": {"fullname": "vertax.BoundedBilevelOptimizer.compute_inner_loss", "modulename": "vertax", "qualname": "BoundedBilevelOptimizer.compute_inner_loss", "kind": "function", "doc": "Get the result of self.loss_function_inner called with the correct arguments.
\n\nMust be implemented by child classes.
\n", "signature": "(\tself , \tmesh : vertax . meshes . mesh . Mesh , \tonly_on_vertices : None | list [ int ] = None , \tonly_on_edges : None | list [ int ] = None , \tonly_on_faces : None | list [ int ] = None ) -> float : ", "funcdef": "def"}, "vertax.plot_mesh": {"fullname": "vertax.plot_mesh", "modulename": "vertax", "qualname": "plot_mesh", "kind": "function", "doc": "Plot the mesh and decide to save and/or show the mesh or not.
\n", "signature": "(\tmesh : vertax . meshes . mesh . Mesh , \tvertex_plot : vertax . meshes . plot . VertexPlot = < VertexPlot . INVISIBLE : 3 > , \tedge_plot : vertax . meshes . plot . EdgePlot = < EdgePlot . BLACK : 1 > , \tface_plot : vertax . meshes . plot . FacePlot = < FacePlot . MULTICOLOR : 1 > , \tvertex_parameters_name : str = '' , \tedge_parameters_name : str = '' , \tface_parameters_name : str = '' , \tshow : bool = True , \tsave : bool = False , \tsave_path : str = 'pbc_mesh.png' , \tfaces_cmap_name : str = 'cividis' , \tedges_cmap_name : str = 'coolwarm' , \tedges_width : float = 2 , \tvertices_cmap_name : str = 'spring' , \tvertices_size : float = 20 , \ttitle : str = '' , \tforced_vertex_scale : tuple [ float , float ] | None = None , \tforced_edge_scale : tuple [ float , float ] | None = None , \tforced_face_scale : tuple [ float , float ] | None = None ) -> None : ", "funcdef": "def"}, "vertax.get_plot_mesh": {"fullname": "vertax.get_plot_mesh", "modulename": "vertax", "qualname": "get_plot_mesh", "kind": "function", "doc": "Get the matplotlib figure and and ax for one plot.
\n", "signature": "(\tmesh : vertax . meshes . mesh . Mesh , \tvertex_plot : vertax . meshes . plot . VertexPlot = < VertexPlot . INVISIBLE : 3 > , \tedge_plot : vertax . meshes . plot . EdgePlot = < EdgePlot . BLACK : 1 > , \tface_plot : vertax . meshes . plot . FacePlot = < FacePlot . MULTICOLOR : 1 > , \tvertex_parameters_name : str = '' , \tedge_parameters_name : str = '' , \tface_parameters_name : str = '' , \tfaces_cmap_name : str = 'cividis' , \tedges_cmap_name : str = 'coolwarm' , \tedges_width : float = 2 , \tvertices_cmap_name : str = 'spring' , \tvertices_size : float = 20 , \ttitle : str = '' , \tforced_vertex_scale : tuple [ float , float ] | None = None , \tforced_edge_scale : tuple [ float , float ] | None = None , \tforced_face_scale : tuple [ float , float ] | None = None ) -> tuple [ matplotlib . figure . Figure , matplotlib . axes . _axes . Axes ] : ", "funcdef": "def"}, "vertax.FacePlot": {"fullname": "vertax.FacePlot", "modulename": "vertax", "qualname": "FacePlot", "kind": "class", "doc": "What it is possible to show on a face.
\n", "bases": "enum.Enum"}, "vertax.FacePlot.MULTICOLOR": {"fullname": "vertax.FacePlot.MULTICOLOR", "modulename": "vertax", "qualname": "FacePlot.MULTICOLOR", "kind": "variable", "doc": "Each face get a random color.
\n", "default_value": "<FacePlot.MULTICOLOR: 1>"}, "vertax.FacePlot.FACE_PARAMETER": {"fullname": "vertax.FacePlot.FACE_PARAMETER", "modulename": "vertax", "qualname": "FacePlot.FACE_PARAMETER", "kind": "variable", "doc": "The face color depends on its parameter.
\n", "default_value": "<FacePlot.FACE_PARAMETER: 2>"}, "vertax.FacePlot.AREA": {"fullname": "vertax.FacePlot.AREA", "modulename": "vertax", "qualname": "FacePlot.AREA", "kind": "variable", "doc": "The face color depends on its area.
\n", "default_value": "<FacePlot.AREA: 3>"}, "vertax.FacePlot.PERIMETER": {"fullname": "vertax.FacePlot.PERIMETER", "modulename": "vertax", "qualname": "FacePlot.PERIMETER", "kind": "variable", "doc": "The face color depends on its perimeter.
\n", "default_value": "<FacePlot.PERIMETER: 4>"}, "vertax.FacePlot.WHITE": {"fullname": "vertax.FacePlot.WHITE", "modulename": "vertax", "qualname": "FacePlot.WHITE", "kind": "variable", "doc": "All faces are just white.
\n", "default_value": "<FacePlot.WHITE: 5>"}, "vertax.FacePlot.FATES": {"fullname": "vertax.FacePlot.FATES", "modulename": "vertax", "qualname": "FacePlot.FATES", "kind": "variable", "doc": "Faces are colored depending on their fate marker (if any).
\n", "default_value": "<FacePlot.FATES: 6>"}, "vertax.EdgePlot": {"fullname": "vertax.EdgePlot", "modulename": "vertax", "qualname": "EdgePlot", "kind": "class", "doc": "What it is possible to show on an edge.
\n", "bases": "enum.Enum"}, "vertax.EdgePlot.BLACK": {"fullname": "vertax.EdgePlot.BLACK", "modulename": "vertax", "qualname": "EdgePlot.BLACK", "kind": "variable", "doc": "All edges are black.
\n", "default_value": "<EdgePlot.BLACK: 1>"}, "vertax.EdgePlot.EDGE_PARAMETER": {"fullname": "vertax.EdgePlot.EDGE_PARAMETER", "modulename": "vertax", "qualname": "EdgePlot.EDGE_PARAMETER", "kind": "variable", "doc": "The edge color depends on its parameter.
\n", "default_value": "<EdgePlot.EDGE_PARAMETER: 2>"}, "vertax.EdgePlot.LENGTH": {"fullname": "vertax.EdgePlot.LENGTH", "modulename": "vertax", "qualname": "EdgePlot.LENGTH", "kind": "variable", "doc": "The edge color depends on its length.
\n", "default_value": "<EdgePlot.LENGTH: 3>"}, "vertax.EdgePlot.INVISIBLE": {"fullname": "vertax.EdgePlot.INVISIBLE", "modulename": "vertax", "qualname": "EdgePlot.INVISIBLE", "kind": "variable", "doc": "Do not show edges.
\n", "default_value": "<EdgePlot.INVISIBLE: 4>"}, "vertax.VertexPlot": {"fullname": "vertax.VertexPlot", "modulename": "vertax", "qualname": "VertexPlot", "kind": "class", "doc": "What it is possible to show on a vertex.
\n", "bases": "enum.Enum"}, "vertax.VertexPlot.BLACK": {"fullname": "vertax.VertexPlot.BLACK", "modulename": "vertax", "qualname": "VertexPlot.BLACK", "kind": "variable", "doc": "Vertices are black.
\n", "default_value": "<VertexPlot.BLACK: 1>"}, "vertax.VertexPlot.VERTEX_PARAMETER": {"fullname": "vertax.VertexPlot.VERTEX_PARAMETER", "modulename": "vertax", "qualname": "VertexPlot.VERTEX_PARAMETER", "kind": "variable", "doc": "The vertex color depends on its parameter.
\n", "default_value": "<VertexPlot.VERTEX_PARAMETER: 2>"}, "vertax.VertexPlot.INVISIBLE": {"fullname": "vertax.VertexPlot.INVISIBLE", "modulename": "vertax", "qualname": "VertexPlot.INVISIBLE", "kind": "variable", "doc": "Do not show vertices.
\n", "default_value": "<VertexPlot.INVISIBLE: 3>"}, "vertax.cost_v2v": {"fullname": "vertax.cost_v2v", "modulename": "vertax", "qualname": "cost_v2v", "kind": "function", "doc": "Cost vertex to vertex. Compare the positions of given vertices to target vertices (PbcMesh).
\n", "signature": "(\tvertTable : jax . Array , \theTable : jax . Array , \tfaceTable : jax . Array , \twidth : float , \theight : float , \tvertTable_target : jax . Array , \t_heTable_target : jax . Array , \t_faceTable_target : jax . Array , \tselected_verts : jax . Array | None = None , \tselected_hes : jax . Array | None = None , \tselected_faces : jax . Array | None = None , \t_image_target : jax . Array | None = None ) -> jax . Array : ", "funcdef": "def"}, "vertax.cost_mesh2image": {"fullname": "vertax.cost_mesh2image", "modulename": "vertax", "qualname": "cost_mesh2image", "kind": "function", "doc": "Cost mesh to image. Compare the given vertices positions to a target image (PbcMesh).
\n", "signature": "(\tvertTable : jax . Array , \theTable : jax . Array , \t_faceTable : jax . Array , \twidth : float , \theight : float , \t_vertTable_target : jax . Array , \t_heTable_target : jax . Array , \t_faceTable_target : jax . Array , \t_selected_verts : jax . Array , \tselected_hes : jax . Array , \t_selected_faces : jax . Array , \timage_target : jax . Array ) -> jax . Array : ", "funcdef": "def"}, "vertax.cost_areas": {"fullname": "vertax.cost_areas", "modulename": "vertax", "qualname": "cost_areas", "kind": "function", "doc": "Cost areas : compare the areas of the given mesh versus the areas of the target mesh (PbcMesh).
\n", "signature": "(\tvertTable : jax . Array , \theTable : jax . Array , \tfaceTable : jax . Array , \twidth : float , \theight : float , \t_selected_verts : jax . Array , \t_selected_hes : jax . Array , \tselected_faces : jax . Array , \tvertTable_target : jax . Array , \theTable_target : jax . Array , \tfaceTable_target : jax . Array , \t_image_target : jax . Array ) -> jax . Array : ", "funcdef": "def"}, "vertax.cost_IAS": {"fullname": "vertax.cost_IAS", "modulename": "vertax", "qualname": "cost_IAS", "kind": "function", "doc": "Differentiable Index Aware Structural Loss. Force to respect the topology.
\n\nC_{IAS}(i,j) = \\sqrt{\\sum_{k=1}^{N} (S_1(i,k) - S_2(j,k))^2}
\n", "signature": "(\tvertTable : jax . Array , \theTable : jax . Array , \tfaceTable : jax . Array , \t_width : float , \t_height : float , \tvertTable_target : jax . Array , \theTable_target : jax . Array , \tfaceTable_target : jax . Array , \t_selected_verts : jax . Array | None = None , \t_selected_hes : jax . Array | None = None , \t_selected_faces : jax . Array | None = None , \t_image_target : jax . Array | None = None ) -> jax . Array : ", "funcdef": "def"}, "vertax.cost_d_IAS": {"fullname": "vertax.cost_d_IAS", "modulename": "vertax", "qualname": "cost_d_IAS", "kind": "function", "doc": "Discrete Index Aware Structural loss. Counts mismatched edges.
\n\nC_{d-IAS}(i,j) = \\sum_{k=1}^{N} |A_1(i,k) - A_2(j,k)|
\n", "signature": "(\t_vertTable : jax . Array , \theTable : jax . Array , \tfaceTable : jax . Array , \t_width : float , \t_height : float , \t_vertTable_target : jax . Array , \theTable_target : jax . Array , \tfaceTable_target : jax . Array , \t_selected_verts : jax . Array | None = None , \t_selected_hes : jax . Array | None = None , \t_selected_faces : jax . Array | None = None , \t_image_target : jax . Array | None = None ) -> int : ", "funcdef": "def"}, "vertax.cost_tem_halfedge": {"fullname": "vertax.cost_tem_halfedge", "modulename": "vertax", "qualname": "cost_tem_halfedge", "kind": "function", "doc": "TEM-inspired loss using half-edge dual graph (cells).
\n", "signature": "(\tvertTable : jax . Array , \theTable : jax . Array , \tfaceTable : jax . Array , \twidth : float , \theight : float , \tvertTable_target : jax . Array , \theTable_target : jax . Array , \tfaceTable_target : jax . Array , \t_selected_verts : jax . Array | None = None , \t_selected_hes : jax . Array | None = None , \t_selected_faces : jax . Array | None = None , \t_image_target : jax . Array | None = None ) -> jax . Array : ", "funcdef": "def"}, "vertax.cost_v2v_ias": {"fullname": "vertax.cost_v2v_ias", "modulename": "vertax", "qualname": "cost_v2v_ias", "kind": "function", "doc": "Mix of cost_v2v and cost_IAS (with weight 0.6 and 0.4).
\n", "signature": "(\tvertTable : jax . Array , \theTable : jax . Array , \tfaceTable : jax . Array , \twidth : float , \theight : float , \tvertTable_target : jax . Array , \theTable_target : jax . Array , \tfaceTable_target : jax . Array , \t_selected_verts : jax . Array | None = None , \t_selected_hes : jax . Array | None = None , \t_selected_faces : jax . Array | None = None , \t_image_target : jax . Array | None = None ) -> jax . Array : ", "funcdef": "def"}, "vertax.cost_v2v_tem": {"fullname": "vertax.cost_v2v_tem", "modulename": "vertax", "qualname": "cost_v2v_tem", "kind": "function", "doc": "Mix of cost_v2v and cost_tem_halfedge (with respective weights 0.99 and 0.01).
\n", "signature": "(\tvertTable : jax . Array , \theTable : jax . Array , \tfaceTable : jax . Array , \twidth : float , \theight : float , \tvertTable_target : jax . Array , \theTable_target : jax . Array , \tfaceTable_target : jax . Array , \t_selected_verts : jax . Array | None = None , \t_selected_hes : jax . Array | None = None , \t_selected_faces : jax . Array | None = None , \t_image_target : jax . Array | None = None ) -> jax . Array : ", "funcdef": "def"}, "vertax.cost_ratio": {"fullname": "vertax.cost_ratio", "modulename": "vertax", "qualname": "cost_ratio", "kind": "function", "doc": "Cost that nudges a BoundedMesh to elongate along one axis while narrowing along the orthogonal axis.
\n", "signature": "(\tvertTable : jax . Array , \t_angTable : jax . Array | None = None , \t_heTable : jax . Array | None = None , \t_faceTable : jax . Array | None = None , \t_vertTable_target : jax . Array | None = None , \t_angTable_target : jax . Array | None = None , \t_heTable_target : jax . Array | None = None , \t_faceTable_target : jax . Array | None = None , \t_selected_verts : jax . Array | None = None , \t_selected_hes : jax . Array | None = None , \t_selected_faces : jax . Array | None = None , \t_image_target : jax . Array | None = None ) -> jax . Array : ", "funcdef": "def"}, "vertax.cost_checkerboard": {"fullname": "vertax.cost_checkerboard", "modulename": "vertax", "qualname": "cost_checkerboard", "kind": "function", "doc": "Cost that nudges a BoundedMesh with different fated cells to avoid having neighboring cells with same fate.
\n\nThis leads to a checkerboard pattern when there is 2 fates.
\n", "signature": "(\tvertTable : jax . Array , \t_angTable : jax . Array , \theTable : jax . Array , \tfaceTable : jax . Array , \t_vertTable_target : jax . Array | None = None , \t_angTable_target : jax . Array | None = None , \t_heTable_target : jax . Array | None = None , \t_faceTable_target : jax . Array | None = None , \t_selected_verts : jax . Array | None = None , \t_selected_hes : jax . Array | None = None , \t_selected_faces : jax . Array | None = None , \t_image_target : jax . Array | None = None ) -> jax . Array : ", "funcdef": "def"}, "vertax.energy_shape_factor_hetero": {"fullname": "vertax.energy_shape_factor_hetero", "modulename": "vertax", "qualname": "energy_shape_factor_hetero", "kind": "function", "doc": "E1 energy where the shape factor depends on the cell.
\n", "signature": "(\tvertTable : jax . Array , \theTable : jax . Array , \tfaceTable : jax . Array , \twidth : float , \theight : float , \tselected_faces : jax . Array , \tface_params : jax . Array ) -> jax . Array : ", "funcdef": "def"}, "vertax.energy_shape_factor_homo": {"fullname": "vertax.energy_shape_factor_homo", "modulename": "vertax", "qualname": "energy_shape_factor_homo", "kind": "function", "doc": "E1 energy where the shape factor is uniform (give only one face_params, it will be broadcasted).
\n", "signature": "(\tvertTable : jax . Array , \theTable : jax . Array , \tfaceTable : jax . Array , \twidth : float , \theight : float , \tface_params : jax . Array ) -> jax . Array : ", "funcdef": "def"}, "vertax.energy_line_tensions": {"fullname": "vertax.energy_line_tensions", "modulename": "vertax", "qualname": "energy_line_tensions", "kind": "function", "doc": "E2 energy for PBC meshes, elastic penalty on cell areas and line tension term weighted by edge lengths.
\n", "signature": "(\tvertTable : jax . Array , \theTable : jax . Array , \tfaceTable : jax . Array , \twidth : float , \theight : float , \the_params : jax . Array , \tface_params : jax . Array ) -> jax . Array : ", "funcdef": "def"}, "vertax.energy_line_tensions_bounded": {"fullname": "vertax.energy_line_tensions_bounded", "modulename": "vertax", "qualname": "energy_line_tensions_bounded", "kind": "function", "doc": "E2 energy for bounded meshes, elastic penalty on cell areas and line tension term weighted by edge lengths.
\n", "signature": "(\tvertTable : jax . Array , \tangTable : jax . Array , \theTable : jax . Array , \tfaceTable : jax . Array , \t_selected_verts : jax . Array | None , \t_selected_hes : jax . Array | None , \t_selected_faces : jax . Array | None , \t_vert_params : jax . Array , \the_params : jax . Array , \t_face_params : jax . Array ) -> jax . Array : ", "funcdef": "def"}}, "docInfo": {"vertax": {"qualname": 0, "fullname": 1, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 550}, "vertax.Mesh": {"qualname": 1, "fullname": 2, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 34}, "vertax.Mesh.__init__": {"qualname": 3, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 4, "bases": 0, "doc": 40}, "vertax.Mesh.faces": {"qualname": 2, "fullname": 3, "annotation": 3, "default_value": 0, "signature": 0, "bases": 0, "doc": 8}, "vertax.Mesh.edges": {"qualname": 2, "fullname": 3, "annotation": 3, "default_value": 0, "signature": 0, "bases": 0, "doc": 10}, "vertax.Mesh.vertices": {"qualname": 2, "fullname": 3, "annotation": 3, "default_value": 0, "signature": 0, "bases": 0, "doc": 9}, "vertax.Mesh.width": {"qualname": 2, "fullname": 3, "annotation": 2, "default_value": 0, "signature": 0, "bases": 0, "doc": 17}, "vertax.Mesh.height": {"qualname": 2, "fullname": 3, "annotation": 2, "default_value": 0, "signature": 0, "bases": 0, "doc": 17}, "vertax.Mesh.faces_params": {"qualname": 3, "fullname": 4, "annotation": 3, "default_value": 0, "signature": 0, "bases": 0, "doc": 10}, "vertax.Mesh.edges_params": {"qualname": 3, "fullname": 4, "annotation": 3, "default_value": 0, "signature": 0, "bases": 0, "doc": 10}, "vertax.Mesh.vertices_params": {"qualname": 3, "fullname": 4, "annotation": 3, "default_value": 0, "signature": 0, "bases": 0, "doc": 10}, "vertax.Mesh.nb_faces": {"qualname": 3, "fullname": 4, "annotation": 2, "default_value": 0, "signature": 0, "bases": 0, "doc": 11}, "vertax.Mesh.nb_edges": {"qualname": 3, "fullname": 4, "annotation": 2, "default_value": 0, "signature": 0, "bases": 0, "doc": 11}, "vertax.Mesh.nb_half_edges": {"qualname": 4, "fullname": 5, "annotation": 2, "default_value": 0, "signature": 0, "bases": 0, "doc": 18}, "vertax.Mesh.nb_vertices": {"qualname": 3, "fullname": 4, "annotation": 2, "default_value": 0, "signature": 0, "bases": 0, "doc": 11}, "vertax.Mesh.save_mesh": {"qualname": 3, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 24, "bases": 0, "doc": 40}, "vertax.Mesh.load_mesh": {"qualname": 3, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 24, "bases": 0, "doc": 47}, "vertax.PbcMesh": {"qualname": 1, "fullname": 2, "annotation": 0, "default_value": 0, "signature": 0, "bases": 4, "doc": 175}, "vertax.PbcMesh.__init__": {"qualname": 3, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 4, "bases": 0, "doc": 55}, "vertax.PbcMesh.MAX_EDGES_IN_ANY_FACE": {"qualname": 6, "fullname": 7, "annotation": 2, "default_value": 0, "signature": 0, "bases": 0, "doc": 23}, "vertax.PbcMesh.from_random_seeds": {"qualname": 4, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 56, "bases": 0, "doc": 104}, "vertax.PbcMesh.from_seeds": {"qualname": 3, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 49, "bases": 0, "doc": 88}, "vertax.PbcMesh.from_image": {"qualname": 3, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 61, "bases": 0, "doc": 112}, "vertax.PbcMesh.from_mask": {"qualname": 3, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 61, "bases": 0, "doc": 72}, "vertax.PbcMesh.create_empty": {"qualname": 3, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 15}, "vertax.PbcMesh.copy_mesh": {"qualname": 3, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 25, "bases": 0, "doc": 13}, "vertax.PbcMesh.save_mesh": {"qualname": 3, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 24, "bases": 0, "doc": 43}, "vertax.PbcMesh.load_mesh": {"qualname": 3, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 24, "bases": 0, "doc": 59}, "vertax.PbcMesh.save_mesh_txt": {"qualname": 4, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 197, "bases": 0, "doc": 196}, "vertax.PbcMesh.load_mesh_txt": {"qualname": 4, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 197, "bases": 0, "doc": 201}, "vertax.PbcMesh.get_length": {"qualname": 3, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 36, "bases": 0, "doc": 9}, "vertax.PbcMesh.get_length_with_offset": {"qualname": 5, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 36, "bases": 0, "doc": 21}, "vertax.PbcMesh.get_perimeter": {"qualname": 3, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 35, "bases": 0, "doc": 9}, "vertax.PbcMesh.get_area": {"qualname": 3, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 35, "bases": 0, "doc": 9}, "vertax.PbcMesh.update_boundary_conditions": {"qualname": 4, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 35}, "vertax.BoundedMesh": {"qualname": 1, "fullname": 2, "annotation": 0, "default_value": 0, "signature": 0, "bases": 4, "doc": 235}, "vertax.BoundedMesh.__init__": {"qualname": 3, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 4, "bases": 0, "doc": 8}, "vertax.BoundedMesh.angles": {"qualname": 2, "fullname": 3, "annotation": 3, "default_value": 0, "signature": 0, "bases": 0, "doc": 15}, "vertax.BoundedMesh.nb_angles": {"qualname": 3, "fullname": 4, "annotation": 2, "default_value": 0, "signature": 0, "bases": 0, "doc": 13}, "vertax.BoundedMesh.from_random_seeds": {"qualname": 4, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 80, "bases": 0, "doc": 130}, "vertax.BoundedMesh.from_seeds": {"qualname": 3, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 114, "bases": 0, "doc": 157}, "vertax.BoundedMesh.create_empty": {"qualname": 3, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 15}, "vertax.BoundedMesh.copy_mesh": {"qualname": 3, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 25, "bases": 0, "doc": 13}, "vertax.BoundedMesh.save_mesh": {"qualname": 3, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 24, "bases": 0, "doc": 40}, "vertax.BoundedMesh.load_mesh": {"qualname": 3, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 24, "bases": 0, "doc": 56}, "vertax.BoundedMesh.save_mesh_txt": {"qualname": 4, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 221, "bases": 0, "doc": 212}, "vertax.BoundedMesh.load_mesh_txt": {"qualname": 4, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 221, "bases": 0, "doc": 221}, "vertax.BoundedMesh.get_length": {"qualname": 3, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 36, "bases": 0, "doc": 9}, "vertax.BoundedMesh.get_perimeter": {"qualname": 3, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 35, "bases": 0, "doc": 9}, "vertax.BoundedMesh.get_area": {"qualname": 3, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 35, "bases": 0, "doc": 9}, "vertax.BilevelOptimizationMethod": {"qualname": 1, "fullname": 2, "annotation": 0, "default_value": 0, "signature": 0, "bases": 2, "doc": 13}, "vertax.BilevelOptimizationMethod.AUTOMATIC_DIFFERENTIATION": {"qualname": 3, "fullname": 4, "annotation": 0, "default_value": 10, "signature": 0, "bases": 0, "doc": 25}, "vertax.BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION": {"qualname": 3, "fullname": 4, "annotation": 0, "default_value": 10, "signature": 0, "bases": 0, "doc": 24}, "vertax.BilevelOptimizationMethod.IMPLICIT_DIFFERENTIATION": {"qualname": 3, "fullname": 4, "annotation": 0, "default_value": 10, "signature": 0, "bases": 0, "doc": 23}, "vertax.BilevelOptimizationMethod.ADJOINT_STATE": {"qualname": 3, "fullname": 4, "annotation": 0, "default_value": 10, "signature": 0, "bases": 0, "doc": 23}, "vertax.PbcBilevelOptimizer": {"qualname": 1, "fullname": 2, "annotation": 0, "default_value": 0, "signature": 0, "bases": 4, "doc": 14}, "vertax.PbcBilevelOptimizer.__init__": {"qualname": 3, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 4, "bases": 0, "doc": 28}, "vertax.PbcBilevelOptimizer.compute_outer_loss": {"qualname": 4, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 137, "bases": 0, "doc": 16}, "vertax.PbcBilevelOptimizer.compute_inner_loss": {"qualname": 4, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 137, "bases": 0, "doc": 16}, "vertax.BoundedBilevelOptimizer": {"qualname": 1, "fullname": 2, "annotation": 0, "default_value": 0, "signature": 0, "bases": 4, "doc": 12}, "vertax.BoundedBilevelOptimizer.__init__": {"qualname": 3, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 4, "bases": 0, "doc": 27}, "vertax.BoundedBilevelOptimizer.angles_target": {"qualname": 3, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "vertax.BoundedBilevelOptimizer.compute_outer_loss": {"qualname": 4, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 137, "bases": 0, "doc": 25}, "vertax.BoundedBilevelOptimizer.compute_inner_loss": {"qualname": 4, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 137, "bases": 0, "doc": 25}, "vertax.plot_mesh": {"qualname": 2, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 566, "bases": 0, "doc": 16}, "vertax.get_plot_mesh": {"qualname": 3, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 543, "bases": 0, "doc": 13}, "vertax.FacePlot": {"qualname": 1, "fullname": 2, "annotation": 0, "default_value": 0, "signature": 0, "bases": 2, "doc": 12}, "vertax.FacePlot.MULTICOLOR": {"qualname": 2, "fullname": 3, "annotation": 0, "default_value": 7, "signature": 0, "bases": 0, "doc": 9}, "vertax.FacePlot.FACE_PARAMETER": {"qualname": 3, "fullname": 4, "annotation": 0, "default_value": 8, "signature": 0, "bases": 0, "doc": 10}, "vertax.FacePlot.AREA": {"qualname": 2, "fullname": 3, "annotation": 0, "default_value": 7, "signature": 0, "bases": 0, "doc": 10}, "vertax.FacePlot.PERIMETER": {"qualname": 2, "fullname": 3, "annotation": 0, "default_value": 7, "signature": 0, "bases": 0, "doc": 10}, "vertax.FacePlot.WHITE": {"qualname": 2, "fullname": 3, "annotation": 0, "default_value": 7, "signature": 0, "bases": 0, "doc": 8}, "vertax.FacePlot.FATES": {"qualname": 2, "fullname": 3, "annotation": 0, "default_value": 7, "signature": 0, "bases": 0, "doc": 13}, "vertax.EdgePlot": {"qualname": 1, "fullname": 2, "annotation": 0, "default_value": 0, "signature": 0, "bases": 2, "doc": 12}, "vertax.EdgePlot.BLACK": {"qualname": 2, "fullname": 3, "annotation": 0, "default_value": 7, "signature": 0, "bases": 0, "doc": 7}, "vertax.EdgePlot.EDGE_PARAMETER": {"qualname": 3, "fullname": 4, "annotation": 0, "default_value": 8, "signature": 0, "bases": 0, "doc": 10}, "vertax.EdgePlot.LENGTH": {"qualname": 2, "fullname": 3, "annotation": 0, "default_value": 7, "signature": 0, "bases": 0, "doc": 10}, "vertax.EdgePlot.INVISIBLE": {"qualname": 2, "fullname": 3, "annotation": 0, "default_value": 7, "signature": 0, "bases": 0, "doc": 7}, "vertax.VertexPlot": {"qualname": 1, "fullname": 2, "annotation": 0, "default_value": 0, "signature": 0, "bases": 2, "doc": 12}, "vertax.VertexPlot.BLACK": {"qualname": 2, "fullname": 3, "annotation": 0, "default_value": 7, "signature": 0, "bases": 0, "doc": 6}, "vertax.VertexPlot.VERTEX_PARAMETER": {"qualname": 3, "fullname": 4, "annotation": 0, "default_value": 8, "signature": 0, "bases": 0, "doc": 10}, "vertax.VertexPlot.INVISIBLE": {"qualname": 2, "fullname": 3, "annotation": 0, "default_value": 7, "signature": 0, "bases": 0, "doc": 7}, "vertax.cost_v2v": {"qualname": 2, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 258, "bases": 0, "doc": 19}, "vertax.cost_mesh2image": {"qualname": 2, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 209, "bases": 0, "doc": 19}, "vertax.cost_areas": {"qualname": 2, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 206, "bases": 0, "doc": 23}, "vertax.cost_IAS": {"qualname": 2, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 261, "bases": 0, "doc": 31}, "vertax.cost_d_IAS": {"qualname": 3, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 258, "bases": 0, "doc": 30}, "vertax.cost_tem_halfedge": {"qualname": 3, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 259, "bases": 0, "doc": 12}, "vertax.cost_v2v_ias": {"qualname": 3, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 259, "bases": 0, "doc": 17}, "vertax.cost_v2v_tem": {"qualname": 3, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 259, "bases": 0, "doc": 19}, "vertax.cost_ratio": {"qualname": 2, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 368, "bases": 0, "doc": 21}, "vertax.cost_checkerboard": {"qualname": 2, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 327, "bases": 0, "doc": 36}, "vertax.energy_shape_factor_hetero": {"qualname": 4, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 118, "bases": 0, "doc": 13}, "vertax.energy_shape_factor_homo": {"qualname": 4, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 101, "bases": 0, "doc": 20}, "vertax.energy_line_tensions": {"qualname": 3, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 118, "bases": 0, "doc": 21}, "vertax.energy_line_tensions_bounded": {"qualname": 4, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 203, "bases": 0, "doc": 21}}, "length": 96, "save": true}, "index": {"qualname": {"root": {"docs": {"vertax.Mesh.__init__": {"tf": 1}, "vertax.PbcMesh.__init__": {"tf": 1}, "vertax.BoundedMesh.__init__": {"tf": 1}, "vertax.PbcBilevelOptimizer.__init__": {"tf": 1}, "vertax.BoundedBilevelOptimizer.__init__": {"tf": 1}}, "df": 5, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "h": {"2": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {"vertax.cost_mesh2image": {"tf": 1}}, "df": 1}}}}}}, "docs": {"vertax.Mesh": {"tf": 1}, "vertax.Mesh.__init__": {"tf": 1}, "vertax.Mesh.faces": {"tf": 1}, "vertax.Mesh.edges": {"tf": 1}, "vertax.Mesh.vertices": {"tf": 1}, "vertax.Mesh.width": {"tf": 1}, "vertax.Mesh.height": {"tf": 1}, "vertax.Mesh.faces_params": {"tf": 1}, "vertax.Mesh.edges_params": {"tf": 1}, "vertax.Mesh.vertices_params": {"tf": 1}, "vertax.Mesh.nb_faces": {"tf": 1}, "vertax.Mesh.nb_edges": {"tf": 1}, "vertax.Mesh.nb_half_edges": {"tf": 1}, "vertax.Mesh.nb_vertices": {"tf": 1}, "vertax.Mesh.save_mesh": {"tf": 1.4142135623730951}, "vertax.Mesh.load_mesh": {"tf": 1.4142135623730951}, "vertax.PbcMesh.copy_mesh": {"tf": 1}, "vertax.PbcMesh.save_mesh": {"tf": 1}, "vertax.PbcMesh.load_mesh": {"tf": 1}, "vertax.PbcMesh.save_mesh_txt": {"tf": 1}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.copy_mesh": {"tf": 1}, "vertax.BoundedMesh.save_mesh": {"tf": 1}, "vertax.BoundedMesh.load_mesh": {"tf": 1}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1}, "vertax.plot_mesh": {"tf": 1}, "vertax.get_plot_mesh": {"tf": 1}}, "df": 28}}}, "a": {"docs": {}, "df": 0, "x": {"docs": {"vertax.PbcMesh.MAX_EDGES_IN_ANY_FACE": {"tf": 1}}, "df": 1}, "s": {"docs": {}, "df": 0, "k": {"docs": {"vertax.PbcMesh.from_mask": {"tf": 1}}, "df": 1}}}, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {"vertax.FacePlot.MULTICOLOR": {"tf": 1}}, "df": 1}}}}}}}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {"vertax.PbcMesh.MAX_EDGES_IN_ANY_FACE": {"tf": 1}}, "df": 1, "i": {"docs": {}, "df": 0, "t": {"docs": {"vertax.Mesh.__init__": {"tf": 1}, "vertax.PbcMesh.__init__": {"tf": 1}, "vertax.BoundedMesh.__init__": {"tf": 1}, "vertax.PbcBilevelOptimizer.__init__": {"tf": 1}, "vertax.BoundedBilevelOptimizer.__init__": {"tf": 1}}, "df": 5}}, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"vertax.PbcBilevelOptimizer.compute_inner_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_inner_loss": {"tf": 1}}, "df": 2}}}, "v": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"vertax.EdgePlot.INVISIBLE": {"tf": 1}, "vertax.VertexPlot.INVISIBLE": {"tf": 1}}, "df": 2}}}}}}}}, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {"vertax.PbcMesh.from_image": {"tf": 1}}, "df": 1}}}, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {"vertax.BilevelOptimizationMethod.IMPLICIT_DIFFERENTIATION": {"tf": 1}}, "df": 1}}}}}}}, "a": {"docs": {}, "df": 0, "s": {"docs": {"vertax.cost_IAS": {"tf": 1}, "vertax.cost_d_IAS": {"tf": 1}, "vertax.cost_v2v_ias": {"tf": 1}}, "df": 3}}}, "f": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {"vertax.PbcMesh.MAX_EDGES_IN_ANY_FACE": {"tf": 1}, "vertax.FacePlot.FACE_PARAMETER": {"tf": 1}}, "df": 2, "s": {"docs": {"vertax.Mesh.faces": {"tf": 1}, "vertax.Mesh.faces_params": {"tf": 1}, "vertax.Mesh.nb_faces": {"tf": 1}}, "df": 3}, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "t": {"docs": {"vertax.FacePlot": {"tf": 1}, "vertax.FacePlot.MULTICOLOR": {"tf": 1}, "vertax.FacePlot.FACE_PARAMETER": {"tf": 1}, "vertax.FacePlot.AREA": {"tf": 1}, "vertax.FacePlot.PERIMETER": {"tf": 1}, "vertax.FacePlot.WHITE": {"tf": 1}, "vertax.FacePlot.FATES": {"tf": 1}}, "df": 7}}}}}, "t": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {"vertax.energy_shape_factor_hetero": {"tf": 1}, "vertax.energy_shape_factor_homo": {"tf": 1}}, "df": 2}}}}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"vertax.FacePlot.FATES": {"tf": 1}}, "df": 1}}}}, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "m": {"docs": {"vertax.PbcMesh.from_random_seeds": {"tf": 1}, "vertax.PbcMesh.from_seeds": {"tf": 1}, "vertax.PbcMesh.from_image": {"tf": 1}, "vertax.PbcMesh.from_mask": {"tf": 1}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}}, "df": 6}}}}, "e": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {"vertax.EdgePlot.EDGE_PARAMETER": {"tf": 1}}, "df": 1, "s": {"docs": {"vertax.Mesh.edges": {"tf": 1}, "vertax.Mesh.edges_params": {"tf": 1}, "vertax.Mesh.nb_edges": {"tf": 1}, "vertax.Mesh.nb_half_edges": {"tf": 1}, "vertax.PbcMesh.MAX_EDGES_IN_ANY_FACE": {"tf": 1}}, "df": 5}, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "t": {"docs": {"vertax.EdgePlot": {"tf": 1}, "vertax.EdgePlot.BLACK": {"tf": 1}, "vertax.EdgePlot.EDGE_PARAMETER": {"tf": 1}, "vertax.EdgePlot.LENGTH": {"tf": 1}, "vertax.EdgePlot.INVISIBLE": {"tf": 1}}, "df": 5}}}}}}}, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "y": {"docs": {"vertax.PbcMesh.create_empty": {"tf": 1}, "vertax.BoundedMesh.create_empty": {"tf": 1}}, "df": 2}}}}, "q": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "m": {"docs": {"vertax.BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION": {"tf": 1}}, "df": 1}}}}}}}}}}, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "y": {"docs": {"vertax.energy_shape_factor_hetero": {"tf": 1}, "vertax.energy_shape_factor_homo": {"tf": 1}, "vertax.energy_line_tensions": {"tf": 1}, "vertax.energy_line_tensions_bounded": {"tf": 1}}, "df": 4}}}}}}, "v": {"2": {"docs": {}, "df": 0, "v": {"docs": {"vertax.cost_v2v": {"tf": 1}, "vertax.cost_v2v_ias": {"tf": 1}, "vertax.cost_v2v_tem": {"tf": 1}}, "df": 3}}, "docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"vertax.Mesh.vertices": {"tf": 1}, "vertax.Mesh.vertices_params": {"tf": 1}, "vertax.Mesh.nb_vertices": {"tf": 1}}, "df": 3}}}}, "e": {"docs": {}, "df": 0, "x": {"docs": {"vertax.VertexPlot.VERTEX_PARAMETER": {"tf": 1}}, "df": 1, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "t": {"docs": {"vertax.VertexPlot": {"tf": 1}, "vertax.VertexPlot.BLACK": {"tf": 1}, "vertax.VertexPlot.VERTEX_PARAMETER": {"tf": 1}, "vertax.VertexPlot.INVISIBLE": {"tf": 1}}, "df": 4}}}}}}}}}}, "w": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"vertax.Mesh.width": {"tf": 1}}, "df": 1}}}, "t": {"docs": {}, "df": 0, "h": {"docs": {"vertax.PbcMesh.get_length_with_offset": {"tf": 1}}, "df": 1}}}, "h": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"vertax.FacePlot.WHITE": {"tf": 1}}, "df": 1}}}}}, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "t": {"docs": {"vertax.Mesh.height": {"tf": 1}}, "df": 1}}}}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {"vertax.energy_shape_factor_hetero": {"tf": 1}}, "df": 1}}}}}, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "f": {"docs": {"vertax.Mesh.nb_half_edges": {"tf": 1}}, "df": 1, "e": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {"vertax.cost_tem_halfedge": {"tf": 1}}, "df": 1}}}}}}}, "o": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "o": {"docs": {"vertax.energy_shape_factor_homo": {"tf": 1}}, "df": 1}}}}, "p": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "s": {"docs": {"vertax.Mesh.faces_params": {"tf": 1}, "vertax.Mesh.edges_params": {"tf": 1}, "vertax.Mesh.vertices_params": {"tf": 1}}, "df": 3}, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"vertax.FacePlot.FACE_PARAMETER": {"tf": 1}, "vertax.EdgePlot.EDGE_PARAMETER": {"tf": 1}, "vertax.VertexPlot.VERTEX_PARAMETER": {"tf": 1}}, "df": 3}}}}}}}}, "b": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "h": {"docs": {"vertax.PbcMesh": {"tf": 1}, "vertax.PbcMesh.__init__": {"tf": 1}, "vertax.PbcMesh.MAX_EDGES_IN_ANY_FACE": {"tf": 1}, "vertax.PbcMesh.from_random_seeds": {"tf": 1}, "vertax.PbcMesh.from_seeds": {"tf": 1}, "vertax.PbcMesh.from_image": {"tf": 1}, "vertax.PbcMesh.from_mask": {"tf": 1}, "vertax.PbcMesh.create_empty": {"tf": 1}, "vertax.PbcMesh.copy_mesh": {"tf": 1}, "vertax.PbcMesh.save_mesh": {"tf": 1}, "vertax.PbcMesh.load_mesh": {"tf": 1}, "vertax.PbcMesh.save_mesh_txt": {"tf": 1}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1}, "vertax.PbcMesh.get_length": {"tf": 1}, "vertax.PbcMesh.get_length_with_offset": {"tf": 1}, "vertax.PbcMesh.get_perimeter": {"tf": 1}, "vertax.PbcMesh.get_area": {"tf": 1}, "vertax.PbcMesh.update_boundary_conditions": {"tf": 1}}, "df": 18}}}}, "b": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "z": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"vertax.PbcBilevelOptimizer": {"tf": 1}, "vertax.PbcBilevelOptimizer.__init__": {"tf": 1}, "vertax.PbcBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.PbcBilevelOptimizer.compute_inner_loss": {"tf": 1}}, "df": 4}}}}}}}}}}}}}}}}}}, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"vertax.PbcMesh.get_perimeter": {"tf": 1}, "vertax.BoundedMesh.get_perimeter": {"tf": 1}, "vertax.FacePlot.PERIMETER": {"tf": 1}}, "df": 3}}}}}}}}, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"vertax.BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION": {"tf": 1}}, "df": 1}}}}}}}}}}, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "t": {"docs": {"vertax.plot_mesh": {"tf": 1}, "vertax.get_plot_mesh": {"tf": 1}}, "df": 2}}}}, "n": {"docs": {}, "df": 0, "b": {"docs": {"vertax.Mesh.nb_faces": {"tf": 1}, "vertax.Mesh.nb_edges": {"tf": 1}, "vertax.Mesh.nb_half_edges": {"tf": 1}, "vertax.Mesh.nb_vertices": {"tf": 1}, "vertax.BoundedMesh.nb_angles": {"tf": 1}}, "df": 5}}, "s": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {"vertax.Mesh.save_mesh": {"tf": 1}, "vertax.PbcMesh.save_mesh": {"tf": 1}, "vertax.PbcMesh.save_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.save_mesh": {"tf": 1}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1}}, "df": 5}}}, "e": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "s": {"docs": {"vertax.PbcMesh.from_random_seeds": {"tf": 1}, "vertax.PbcMesh.from_seeds": {"tf": 1}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}}, "df": 4}}}}, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"vertax.BilevelOptimizationMethod.ADJOINT_STATE": {"tf": 1}}, "df": 1}}}}, "h": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {"vertax.energy_shape_factor_hetero": {"tf": 1}, "vertax.energy_shape_factor_homo": {"tf": 1}}, "df": 2}}}}}, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "d": {"docs": {"vertax.Mesh.load_mesh": {"tf": 1}, "vertax.PbcMesh.load_mesh": {"tf": 1}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.load_mesh": {"tf": 1}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1}}, "df": 5}}, "s": {"docs": {}, "df": 0, "s": {"docs": {"vertax.PbcBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.PbcBilevelOptimizer.compute_inner_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_inner_loss": {"tf": 1}}, "df": 4}}}, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"vertax.PbcMesh.get_length": {"tf": 1}, "vertax.PbcMesh.get_length_with_offset": {"tf": 1}, "vertax.BoundedMesh.get_length": {"tf": 1}, "vertax.EdgePlot.LENGTH": {"tf": 1}}, "df": 4}}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {"vertax.energy_line_tensions": {"tf": 1}, "vertax.energy_line_tensions_bounded": {"tf": 1}}, "df": 2}}}}, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "y": {"docs": {"vertax.PbcMesh.MAX_EDGES_IN_ANY_FACE": {"tf": 1}}, "df": 1}, "g": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"vertax.BoundedMesh.angles": {"tf": 1}, "vertax.BoundedMesh.nb_angles": {"tf": 1}, "vertax.BoundedBilevelOptimizer.angles_target": {"tf": 1}}, "df": 3}}}}}, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {"vertax.PbcMesh.get_area": {"tf": 1}, "vertax.BoundedMesh.get_area": {"tf": 1}, "vertax.FacePlot.AREA": {"tf": 1}}, "df": 3, "s": {"docs": {"vertax.cost_areas": {"tf": 1}}, "df": 1}}}}, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {"vertax.BilevelOptimizationMethod.AUTOMATIC_DIFFERENTIATION": {"tf": 1}}, "df": 1}}}}}}}}, "d": {"docs": {}, "df": 0, "j": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"vertax.BilevelOptimizationMethod.ADJOINT_STATE": {"tf": 1}}, "df": 1}}}}}}}, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "m": {"docs": {"vertax.PbcMesh.from_random_seeds": {"tf": 1}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1}}, "df": 2}}}}, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {"vertax.cost_ratio": {"tf": 1}}, "df": 1}}}}}, "c": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"vertax.PbcMesh.create_empty": {"tf": 1}, "vertax.BoundedMesh.create_empty": {"tf": 1}}, "df": 2}}}}}, "o": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "y": {"docs": {"vertax.PbcMesh.copy_mesh": {"tf": 1}, "vertax.BoundedMesh.copy_mesh": {"tf": 1}}, "df": 2}}, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {"vertax.PbcMesh.update_boundary_conditions": {"tf": 1}}, "df": 1}}}}}}}}, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"vertax.PbcBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.PbcBilevelOptimizer.compute_inner_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_inner_loss": {"tf": 1}}, "df": 4}}}}}, "s": {"docs": {}, "df": 0, "t": {"docs": {"vertax.cost_v2v": {"tf": 1}, "vertax.cost_mesh2image": {"tf": 1}, "vertax.cost_areas": {"tf": 1}, "vertax.cost_IAS": {"tf": 1}, "vertax.cost_d_IAS": {"tf": 1}, "vertax.cost_tem_halfedge": {"tf": 1}, "vertax.cost_v2v_ias": {"tf": 1}, "vertax.cost_v2v_tem": {"tf": 1}, "vertax.cost_ratio": {"tf": 1}, "vertax.cost_checkerboard": {"tf": 1}}, "df": 10}}}, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "d": {"docs": {"vertax.cost_checkerboard": {"tf": 1}}, "df": 1}}}}}}}}}}}}, "t": {"docs": {}, "df": 0, "x": {"docs": {}, "df": 0, "t": {"docs": {"vertax.PbcMesh.save_mesh_txt": {"tf": 1}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1}}, "df": 4}}, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {"vertax.BoundedBilevelOptimizer.angles_target": {"tf": 1}}, "df": 1}}}}}, "e": {"docs": {}, "df": 0, "m": {"docs": {"vertax.cost_tem_halfedge": {"tf": 1}, "vertax.cost_v2v_tem": {"tf": 1}}, "df": 2}, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {"vertax.energy_line_tensions": {"tf": 1}, "vertax.energy_line_tensions_bounded": {"tf": 1}}, "df": 2}}}}}}}}, "g": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {"vertax.PbcMesh.get_length": {"tf": 1}, "vertax.PbcMesh.get_length_with_offset": {"tf": 1}, "vertax.PbcMesh.get_perimeter": {"tf": 1}, "vertax.PbcMesh.get_area": {"tf": 1}, "vertax.BoundedMesh.get_length": {"tf": 1}, "vertax.BoundedMesh.get_perimeter": {"tf": 1}, "vertax.BoundedMesh.get_area": {"tf": 1}, "vertax.get_plot_mesh": {"tf": 1}}, "df": 8}}}, "o": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {"vertax.PbcMesh.get_length_with_offset": {"tf": 1}}, "df": 1}}}}}, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"vertax.PbcBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_outer_loss": {"tf": 1}}, "df": 2}}}}}, "u": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"vertax.PbcMesh.update_boundary_conditions": {"tf": 1}}, "df": 1}}}}}}, "b": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "y": {"docs": {"vertax.PbcMesh.update_boundary_conditions": {"tf": 1}}, "df": 1}}}, "e": {"docs": {}, "df": 0, "d": {"docs": {"vertax.energy_line_tensions_bounded": {"tf": 1}}, "df": 1, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "h": {"docs": {"vertax.BoundedMesh": {"tf": 1}, "vertax.BoundedMesh.__init__": {"tf": 1}, "vertax.BoundedMesh.angles": {"tf": 1}, "vertax.BoundedMesh.nb_angles": {"tf": 1}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}, "vertax.BoundedMesh.create_empty": {"tf": 1}, "vertax.BoundedMesh.copy_mesh": {"tf": 1}, "vertax.BoundedMesh.save_mesh": {"tf": 1}, "vertax.BoundedMesh.load_mesh": {"tf": 1}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.get_length": {"tf": 1}, "vertax.BoundedMesh.get_perimeter": {"tf": 1}, "vertax.BoundedMesh.get_area": {"tf": 1}}, "df": 15}}}}, "b": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "z": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"vertax.BoundedBilevelOptimizer": {"tf": 1}, "vertax.BoundedBilevelOptimizer.__init__": {"tf": 1}, "vertax.BoundedBilevelOptimizer.angles_target": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_inner_loss": {"tf": 1}}, "df": 5}}}}}}}}}}}}}}}}}}}}}}, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "z": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {"vertax.BilevelOptimizationMethod": {"tf": 1}, "vertax.BilevelOptimizationMethod.AUTOMATIC_DIFFERENTIATION": {"tf": 1}, "vertax.BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION": {"tf": 1}, "vertax.BilevelOptimizationMethod.IMPLICIT_DIFFERENTIATION": {"tf": 1}, "vertax.BilevelOptimizationMethod.ADJOINT_STATE": {"tf": 1}}, "df": 5}}}}}}}}}}}}}}}}}}}}}}}}, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "k": {"docs": {"vertax.EdgePlot.BLACK": {"tf": 1}, "vertax.VertexPlot.BLACK": {"tf": 1}}, "df": 2}}}}}, "d": {"docs": {"vertax.cost_d_IAS": {"tf": 1}}, "df": 1, "i": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"vertax.BilevelOptimizationMethod.AUTOMATIC_DIFFERENTIATION": {"tf": 1}, "vertax.BilevelOptimizationMethod.IMPLICIT_DIFFERENTIATION": {"tf": 1}}, "df": 2}}}}}}}}}}}}}}}}}, "fullname": {"root": {"docs": {"vertax.Mesh.__init__": {"tf": 1}, "vertax.PbcMesh.__init__": {"tf": 1}, "vertax.BoundedMesh.__init__": {"tf": 1}, "vertax.PbcBilevelOptimizer.__init__": {"tf": 1}, "vertax.BoundedBilevelOptimizer.__init__": {"tf": 1}}, "df": 5, "v": {"2": {"docs": {}, "df": 0, "v": {"docs": {"vertax.cost_v2v": {"tf": 1}, "vertax.cost_v2v_ias": {"tf": 1}, "vertax.cost_v2v_tem": {"tf": 1}}, "df": 3}}, "docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "x": {"docs": {"vertax": {"tf": 1}, "vertax.Mesh": {"tf": 1}, "vertax.Mesh.__init__": {"tf": 1}, "vertax.Mesh.faces": {"tf": 1}, "vertax.Mesh.edges": {"tf": 1}, "vertax.Mesh.vertices": {"tf": 1}, "vertax.Mesh.width": {"tf": 1}, "vertax.Mesh.height": {"tf": 1}, "vertax.Mesh.faces_params": {"tf": 1}, "vertax.Mesh.edges_params": {"tf": 1}, "vertax.Mesh.vertices_params": {"tf": 1}, "vertax.Mesh.nb_faces": {"tf": 1}, "vertax.Mesh.nb_edges": {"tf": 1}, "vertax.Mesh.nb_half_edges": {"tf": 1}, "vertax.Mesh.nb_vertices": {"tf": 1}, "vertax.Mesh.save_mesh": {"tf": 1}, "vertax.Mesh.load_mesh": {"tf": 1}, "vertax.PbcMesh": {"tf": 1}, "vertax.PbcMesh.__init__": {"tf": 1}, "vertax.PbcMesh.MAX_EDGES_IN_ANY_FACE": {"tf": 1}, "vertax.PbcMesh.from_random_seeds": {"tf": 1}, "vertax.PbcMesh.from_seeds": {"tf": 1}, "vertax.PbcMesh.from_image": {"tf": 1}, "vertax.PbcMesh.from_mask": {"tf": 1}, "vertax.PbcMesh.create_empty": {"tf": 1}, "vertax.PbcMesh.copy_mesh": {"tf": 1}, "vertax.PbcMesh.save_mesh": {"tf": 1}, "vertax.PbcMesh.load_mesh": {"tf": 1}, "vertax.PbcMesh.save_mesh_txt": {"tf": 1}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1}, "vertax.PbcMesh.get_length": {"tf": 1}, "vertax.PbcMesh.get_length_with_offset": {"tf": 1}, "vertax.PbcMesh.get_perimeter": {"tf": 1}, "vertax.PbcMesh.get_area": {"tf": 1}, "vertax.PbcMesh.update_boundary_conditions": {"tf": 1}, "vertax.BoundedMesh": {"tf": 1}, "vertax.BoundedMesh.__init__": {"tf": 1}, "vertax.BoundedMesh.angles": {"tf": 1}, "vertax.BoundedMesh.nb_angles": {"tf": 1}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}, "vertax.BoundedMesh.create_empty": {"tf": 1}, "vertax.BoundedMesh.copy_mesh": {"tf": 1}, "vertax.BoundedMesh.save_mesh": {"tf": 1}, "vertax.BoundedMesh.load_mesh": {"tf": 1}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.get_length": {"tf": 1}, "vertax.BoundedMesh.get_perimeter": {"tf": 1}, "vertax.BoundedMesh.get_area": {"tf": 1}, "vertax.BilevelOptimizationMethod": {"tf": 1}, "vertax.BilevelOptimizationMethod.AUTOMATIC_DIFFERENTIATION": {"tf": 1}, "vertax.BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION": {"tf": 1}, "vertax.BilevelOptimizationMethod.IMPLICIT_DIFFERENTIATION": {"tf": 1}, "vertax.BilevelOptimizationMethod.ADJOINT_STATE": {"tf": 1}, "vertax.PbcBilevelOptimizer": {"tf": 1}, "vertax.PbcBilevelOptimizer.__init__": {"tf": 1}, "vertax.PbcBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.PbcBilevelOptimizer.compute_inner_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer": {"tf": 1}, "vertax.BoundedBilevelOptimizer.__init__": {"tf": 1}, "vertax.BoundedBilevelOptimizer.angles_target": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_inner_loss": {"tf": 1}, "vertax.plot_mesh": {"tf": 1}, "vertax.get_plot_mesh": {"tf": 1}, "vertax.FacePlot": {"tf": 1}, "vertax.FacePlot.MULTICOLOR": {"tf": 1}, "vertax.FacePlot.FACE_PARAMETER": {"tf": 1}, "vertax.FacePlot.AREA": {"tf": 1}, "vertax.FacePlot.PERIMETER": {"tf": 1}, "vertax.FacePlot.WHITE": {"tf": 1}, "vertax.FacePlot.FATES": {"tf": 1}, "vertax.EdgePlot": {"tf": 1}, "vertax.EdgePlot.BLACK": {"tf": 1}, "vertax.EdgePlot.EDGE_PARAMETER": {"tf": 1}, "vertax.EdgePlot.LENGTH": {"tf": 1}, "vertax.EdgePlot.INVISIBLE": {"tf": 1}, "vertax.VertexPlot": {"tf": 1}, "vertax.VertexPlot.BLACK": {"tf": 1}, "vertax.VertexPlot.VERTEX_PARAMETER": {"tf": 1}, "vertax.VertexPlot.INVISIBLE": {"tf": 1}, "vertax.cost_v2v": {"tf": 1}, "vertax.cost_mesh2image": {"tf": 1}, "vertax.cost_areas": {"tf": 1}, "vertax.cost_IAS": {"tf": 1}, "vertax.cost_d_IAS": {"tf": 1}, "vertax.cost_tem_halfedge": {"tf": 1}, "vertax.cost_v2v_ias": {"tf": 1}, "vertax.cost_v2v_tem": {"tf": 1}, "vertax.cost_ratio": {"tf": 1}, "vertax.cost_checkerboard": {"tf": 1}, "vertax.energy_shape_factor_hetero": {"tf": 1}, "vertax.energy_shape_factor_homo": {"tf": 1}, "vertax.energy_line_tensions": {"tf": 1}, "vertax.energy_line_tensions_bounded": {"tf": 1}}, "df": 96}}, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"vertax.Mesh.vertices": {"tf": 1}, "vertax.Mesh.vertices_params": {"tf": 1}, "vertax.Mesh.nb_vertices": {"tf": 1}}, "df": 3}}}}, "e": {"docs": {}, "df": 0, "x": {"docs": {"vertax.VertexPlot.VERTEX_PARAMETER": {"tf": 1}}, "df": 1, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "t": {"docs": {"vertax.VertexPlot": {"tf": 1}, "vertax.VertexPlot.BLACK": {"tf": 1}, "vertax.VertexPlot.VERTEX_PARAMETER": {"tf": 1}, "vertax.VertexPlot.INVISIBLE": {"tf": 1}}, "df": 4}}}}}}}}}}, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "h": {"2": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {"vertax.cost_mesh2image": {"tf": 1}}, "df": 1}}}}}}, "docs": {"vertax.Mesh": {"tf": 1}, "vertax.Mesh.__init__": {"tf": 1}, "vertax.Mesh.faces": {"tf": 1}, "vertax.Mesh.edges": {"tf": 1}, "vertax.Mesh.vertices": {"tf": 1}, "vertax.Mesh.width": {"tf": 1}, "vertax.Mesh.height": {"tf": 1}, "vertax.Mesh.faces_params": {"tf": 1}, "vertax.Mesh.edges_params": {"tf": 1}, "vertax.Mesh.vertices_params": {"tf": 1}, "vertax.Mesh.nb_faces": {"tf": 1}, "vertax.Mesh.nb_edges": {"tf": 1}, "vertax.Mesh.nb_half_edges": {"tf": 1}, "vertax.Mesh.nb_vertices": {"tf": 1}, "vertax.Mesh.save_mesh": {"tf": 1.4142135623730951}, "vertax.Mesh.load_mesh": {"tf": 1.4142135623730951}, "vertax.PbcMesh.copy_mesh": {"tf": 1}, "vertax.PbcMesh.save_mesh": {"tf": 1}, "vertax.PbcMesh.load_mesh": {"tf": 1}, "vertax.PbcMesh.save_mesh_txt": {"tf": 1}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.copy_mesh": {"tf": 1}, "vertax.BoundedMesh.save_mesh": {"tf": 1}, "vertax.BoundedMesh.load_mesh": {"tf": 1}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1}, "vertax.plot_mesh": {"tf": 1}, "vertax.get_plot_mesh": {"tf": 1}}, "df": 28}}}, "a": {"docs": {}, "df": 0, "x": {"docs": {"vertax.PbcMesh.MAX_EDGES_IN_ANY_FACE": {"tf": 1}}, "df": 1}, "s": {"docs": {}, "df": 0, "k": {"docs": {"vertax.PbcMesh.from_mask": {"tf": 1}}, "df": 1}}}, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {"vertax.FacePlot.MULTICOLOR": {"tf": 1}}, "df": 1}}}}}}}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {"vertax.PbcMesh.MAX_EDGES_IN_ANY_FACE": {"tf": 1}}, "df": 1, "i": {"docs": {}, "df": 0, "t": {"docs": {"vertax.Mesh.__init__": {"tf": 1}, "vertax.PbcMesh.__init__": {"tf": 1}, "vertax.BoundedMesh.__init__": {"tf": 1}, "vertax.PbcBilevelOptimizer.__init__": {"tf": 1}, "vertax.BoundedBilevelOptimizer.__init__": {"tf": 1}}, "df": 5}}, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"vertax.PbcBilevelOptimizer.compute_inner_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_inner_loss": {"tf": 1}}, "df": 2}}}, "v": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"vertax.EdgePlot.INVISIBLE": {"tf": 1}, "vertax.VertexPlot.INVISIBLE": {"tf": 1}}, "df": 2}}}}}}}}, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {"vertax.PbcMesh.from_image": {"tf": 1}}, "df": 1}}}, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {"vertax.BilevelOptimizationMethod.IMPLICIT_DIFFERENTIATION": {"tf": 1}}, "df": 1}}}}}}}, "a": {"docs": {}, "df": 0, "s": {"docs": {"vertax.cost_IAS": {"tf": 1}, "vertax.cost_d_IAS": {"tf": 1}, "vertax.cost_v2v_ias": {"tf": 1}}, "df": 3}}}, "f": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {"vertax.PbcMesh.MAX_EDGES_IN_ANY_FACE": {"tf": 1}, "vertax.FacePlot.FACE_PARAMETER": {"tf": 1}}, "df": 2, "s": {"docs": {"vertax.Mesh.faces": {"tf": 1}, "vertax.Mesh.faces_params": {"tf": 1}, "vertax.Mesh.nb_faces": {"tf": 1}}, "df": 3}, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "t": {"docs": {"vertax.FacePlot": {"tf": 1}, "vertax.FacePlot.MULTICOLOR": {"tf": 1}, "vertax.FacePlot.FACE_PARAMETER": {"tf": 1}, "vertax.FacePlot.AREA": {"tf": 1}, "vertax.FacePlot.PERIMETER": {"tf": 1}, "vertax.FacePlot.WHITE": {"tf": 1}, "vertax.FacePlot.FATES": {"tf": 1}}, "df": 7}}}}}, "t": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {"vertax.energy_shape_factor_hetero": {"tf": 1}, "vertax.energy_shape_factor_homo": {"tf": 1}}, "df": 2}}}}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"vertax.FacePlot.FATES": {"tf": 1}}, "df": 1}}}}, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "m": {"docs": {"vertax.PbcMesh.from_random_seeds": {"tf": 1}, "vertax.PbcMesh.from_seeds": {"tf": 1}, "vertax.PbcMesh.from_image": {"tf": 1}, "vertax.PbcMesh.from_mask": {"tf": 1}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}}, "df": 6}}}}, "e": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {"vertax.EdgePlot.EDGE_PARAMETER": {"tf": 1}}, "df": 1, "s": {"docs": {"vertax.Mesh.edges": {"tf": 1}, "vertax.Mesh.edges_params": {"tf": 1}, "vertax.Mesh.nb_edges": {"tf": 1}, "vertax.Mesh.nb_half_edges": {"tf": 1}, "vertax.PbcMesh.MAX_EDGES_IN_ANY_FACE": {"tf": 1}}, "df": 5}, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "t": {"docs": {"vertax.EdgePlot": {"tf": 1}, "vertax.EdgePlot.BLACK": {"tf": 1}, "vertax.EdgePlot.EDGE_PARAMETER": {"tf": 1}, "vertax.EdgePlot.LENGTH": {"tf": 1}, "vertax.EdgePlot.INVISIBLE": {"tf": 1}}, "df": 5}}}}}}}, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "y": {"docs": {"vertax.PbcMesh.create_empty": {"tf": 1}, "vertax.BoundedMesh.create_empty": {"tf": 1}}, "df": 2}}}}, "q": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "m": {"docs": {"vertax.BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION": {"tf": 1}}, "df": 1}}}}}}}}}}, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "y": {"docs": {"vertax.energy_shape_factor_hetero": {"tf": 1}, "vertax.energy_shape_factor_homo": {"tf": 1}, "vertax.energy_line_tensions": {"tf": 1}, "vertax.energy_line_tensions_bounded": {"tf": 1}}, "df": 4}}}}}}, "w": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"vertax.Mesh.width": {"tf": 1}}, "df": 1}}}, "t": {"docs": {}, "df": 0, "h": {"docs": {"vertax.PbcMesh.get_length_with_offset": {"tf": 1}}, "df": 1}}}, "h": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"vertax.FacePlot.WHITE": {"tf": 1}}, "df": 1}}}}}, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "t": {"docs": {"vertax.Mesh.height": {"tf": 1}}, "df": 1}}}}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {"vertax.energy_shape_factor_hetero": {"tf": 1}}, "df": 1}}}}}, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "f": {"docs": {"vertax.Mesh.nb_half_edges": {"tf": 1}}, "df": 1, "e": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {"vertax.cost_tem_halfedge": {"tf": 1}}, "df": 1}}}}}}}, "o": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "o": {"docs": {"vertax.energy_shape_factor_homo": {"tf": 1}}, "df": 1}}}}, "p": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "s": {"docs": {"vertax.Mesh.faces_params": {"tf": 1}, "vertax.Mesh.edges_params": {"tf": 1}, "vertax.Mesh.vertices_params": {"tf": 1}}, "df": 3}, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"vertax.FacePlot.FACE_PARAMETER": {"tf": 1}, "vertax.EdgePlot.EDGE_PARAMETER": {"tf": 1}, "vertax.VertexPlot.VERTEX_PARAMETER": {"tf": 1}}, "df": 3}}}}}}}}, "b": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "h": {"docs": {"vertax.PbcMesh": {"tf": 1}, "vertax.PbcMesh.__init__": {"tf": 1}, "vertax.PbcMesh.MAX_EDGES_IN_ANY_FACE": {"tf": 1}, "vertax.PbcMesh.from_random_seeds": {"tf": 1}, "vertax.PbcMesh.from_seeds": {"tf": 1}, "vertax.PbcMesh.from_image": {"tf": 1}, "vertax.PbcMesh.from_mask": {"tf": 1}, "vertax.PbcMesh.create_empty": {"tf": 1}, "vertax.PbcMesh.copy_mesh": {"tf": 1}, "vertax.PbcMesh.save_mesh": {"tf": 1}, "vertax.PbcMesh.load_mesh": {"tf": 1}, "vertax.PbcMesh.save_mesh_txt": {"tf": 1}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1}, "vertax.PbcMesh.get_length": {"tf": 1}, "vertax.PbcMesh.get_length_with_offset": {"tf": 1}, "vertax.PbcMesh.get_perimeter": {"tf": 1}, "vertax.PbcMesh.get_area": {"tf": 1}, "vertax.PbcMesh.update_boundary_conditions": {"tf": 1}}, "df": 18}}}}, "b": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "z": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"vertax.PbcBilevelOptimizer": {"tf": 1}, "vertax.PbcBilevelOptimizer.__init__": {"tf": 1}, "vertax.PbcBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.PbcBilevelOptimizer.compute_inner_loss": {"tf": 1}}, "df": 4}}}}}}}}}}}}}}}}}}, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"vertax.PbcMesh.get_perimeter": {"tf": 1}, "vertax.BoundedMesh.get_perimeter": {"tf": 1}, "vertax.FacePlot.PERIMETER": {"tf": 1}}, "df": 3}}}}}}}}, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"vertax.BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION": {"tf": 1}}, "df": 1}}}}}}}}}}, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "t": {"docs": {"vertax.plot_mesh": {"tf": 1}, "vertax.get_plot_mesh": {"tf": 1}}, "df": 2}}}}, "n": {"docs": {}, "df": 0, "b": {"docs": {"vertax.Mesh.nb_faces": {"tf": 1}, "vertax.Mesh.nb_edges": {"tf": 1}, "vertax.Mesh.nb_half_edges": {"tf": 1}, "vertax.Mesh.nb_vertices": {"tf": 1}, "vertax.BoundedMesh.nb_angles": {"tf": 1}}, "df": 5}}, "s": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {"vertax.Mesh.save_mesh": {"tf": 1}, "vertax.PbcMesh.save_mesh": {"tf": 1}, "vertax.PbcMesh.save_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.save_mesh": {"tf": 1}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1}}, "df": 5}}}, "e": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "s": {"docs": {"vertax.PbcMesh.from_random_seeds": {"tf": 1}, "vertax.PbcMesh.from_seeds": {"tf": 1}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}}, "df": 4}}}}, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"vertax.BilevelOptimizationMethod.ADJOINT_STATE": {"tf": 1}}, "df": 1}}}}, "h": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {"vertax.energy_shape_factor_hetero": {"tf": 1}, "vertax.energy_shape_factor_homo": {"tf": 1}}, "df": 2}}}}}, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "d": {"docs": {"vertax.Mesh.load_mesh": {"tf": 1}, "vertax.PbcMesh.load_mesh": {"tf": 1}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.load_mesh": {"tf": 1}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1}}, "df": 5}}, "s": {"docs": {}, "df": 0, "s": {"docs": {"vertax.PbcBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.PbcBilevelOptimizer.compute_inner_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_inner_loss": {"tf": 1}}, "df": 4}}}, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"vertax.PbcMesh.get_length": {"tf": 1}, "vertax.PbcMesh.get_length_with_offset": {"tf": 1}, "vertax.BoundedMesh.get_length": {"tf": 1}, "vertax.EdgePlot.LENGTH": {"tf": 1}}, "df": 4}}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {"vertax.energy_line_tensions": {"tf": 1}, "vertax.energy_line_tensions_bounded": {"tf": 1}}, "df": 2}}}}, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "y": {"docs": {"vertax.PbcMesh.MAX_EDGES_IN_ANY_FACE": {"tf": 1}}, "df": 1}, "g": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"vertax.BoundedMesh.angles": {"tf": 1}, "vertax.BoundedMesh.nb_angles": {"tf": 1}, "vertax.BoundedBilevelOptimizer.angles_target": {"tf": 1}}, "df": 3}}}}}, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {"vertax.PbcMesh.get_area": {"tf": 1}, "vertax.BoundedMesh.get_area": {"tf": 1}, "vertax.FacePlot.AREA": {"tf": 1}}, "df": 3, "s": {"docs": {"vertax.cost_areas": {"tf": 1}}, "df": 1}}}}, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {"vertax.BilevelOptimizationMethod.AUTOMATIC_DIFFERENTIATION": {"tf": 1}}, "df": 1}}}}}}}}, "d": {"docs": {}, "df": 0, "j": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"vertax.BilevelOptimizationMethod.ADJOINT_STATE": {"tf": 1}}, "df": 1}}}}}}}, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "m": {"docs": {"vertax.PbcMesh.from_random_seeds": {"tf": 1}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1}}, "df": 2}}}}, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {"vertax.cost_ratio": {"tf": 1}}, "df": 1}}}}}, "c": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"vertax.PbcMesh.create_empty": {"tf": 1}, "vertax.BoundedMesh.create_empty": {"tf": 1}}, "df": 2}}}}}, "o": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "y": {"docs": {"vertax.PbcMesh.copy_mesh": {"tf": 1}, "vertax.BoundedMesh.copy_mesh": {"tf": 1}}, "df": 2}}, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {"vertax.PbcMesh.update_boundary_conditions": {"tf": 1}}, "df": 1}}}}}}}}, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"vertax.PbcBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.PbcBilevelOptimizer.compute_inner_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_inner_loss": {"tf": 1}}, "df": 4}}}}}, "s": {"docs": {}, "df": 0, "t": {"docs": {"vertax.cost_v2v": {"tf": 1}, "vertax.cost_mesh2image": {"tf": 1}, "vertax.cost_areas": {"tf": 1}, "vertax.cost_IAS": {"tf": 1}, "vertax.cost_d_IAS": {"tf": 1}, "vertax.cost_tem_halfedge": {"tf": 1}, "vertax.cost_v2v_ias": {"tf": 1}, "vertax.cost_v2v_tem": {"tf": 1}, "vertax.cost_ratio": {"tf": 1}, "vertax.cost_checkerboard": {"tf": 1}}, "df": 10}}}, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "d": {"docs": {"vertax.cost_checkerboard": {"tf": 1}}, "df": 1}}}}}}}}}}}}, "t": {"docs": {}, "df": 0, "x": {"docs": {}, "df": 0, "t": {"docs": {"vertax.PbcMesh.save_mesh_txt": {"tf": 1}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1}}, "df": 4}}, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {"vertax.BoundedBilevelOptimizer.angles_target": {"tf": 1}}, "df": 1}}}}}, "e": {"docs": {}, "df": 0, "m": {"docs": {"vertax.cost_tem_halfedge": {"tf": 1}, "vertax.cost_v2v_tem": {"tf": 1}}, "df": 2}, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {"vertax.energy_line_tensions": {"tf": 1}, "vertax.energy_line_tensions_bounded": {"tf": 1}}, "df": 2}}}}}}}}, "g": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {"vertax.PbcMesh.get_length": {"tf": 1}, "vertax.PbcMesh.get_length_with_offset": {"tf": 1}, "vertax.PbcMesh.get_perimeter": {"tf": 1}, "vertax.PbcMesh.get_area": {"tf": 1}, "vertax.BoundedMesh.get_length": {"tf": 1}, "vertax.BoundedMesh.get_perimeter": {"tf": 1}, "vertax.BoundedMesh.get_area": {"tf": 1}, "vertax.get_plot_mesh": {"tf": 1}}, "df": 8}}}, "o": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {"vertax.PbcMesh.get_length_with_offset": {"tf": 1}}, "df": 1}}}}}, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"vertax.PbcBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_outer_loss": {"tf": 1}}, "df": 2}}}}}, "u": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"vertax.PbcMesh.update_boundary_conditions": {"tf": 1}}, "df": 1}}}}}}, "b": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "y": {"docs": {"vertax.PbcMesh.update_boundary_conditions": {"tf": 1}}, "df": 1}}}, "e": {"docs": {}, "df": 0, "d": {"docs": {"vertax.energy_line_tensions_bounded": {"tf": 1}}, "df": 1, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "h": {"docs": {"vertax.BoundedMesh": {"tf": 1}, "vertax.BoundedMesh.__init__": {"tf": 1}, "vertax.BoundedMesh.angles": {"tf": 1}, "vertax.BoundedMesh.nb_angles": {"tf": 1}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}, "vertax.BoundedMesh.create_empty": {"tf": 1}, "vertax.BoundedMesh.copy_mesh": {"tf": 1}, "vertax.BoundedMesh.save_mesh": {"tf": 1}, "vertax.BoundedMesh.load_mesh": {"tf": 1}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.get_length": {"tf": 1}, "vertax.BoundedMesh.get_perimeter": {"tf": 1}, "vertax.BoundedMesh.get_area": {"tf": 1}}, "df": 15}}}}, "b": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "z": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"vertax.BoundedBilevelOptimizer": {"tf": 1}, "vertax.BoundedBilevelOptimizer.__init__": {"tf": 1}, "vertax.BoundedBilevelOptimizer.angles_target": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_inner_loss": {"tf": 1}}, "df": 5}}}}}}}}}}}}}}}}}}}}}}, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "z": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {"vertax.BilevelOptimizationMethod": {"tf": 1}, "vertax.BilevelOptimizationMethod.AUTOMATIC_DIFFERENTIATION": {"tf": 1}, "vertax.BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION": {"tf": 1}, "vertax.BilevelOptimizationMethod.IMPLICIT_DIFFERENTIATION": {"tf": 1}, "vertax.BilevelOptimizationMethod.ADJOINT_STATE": {"tf": 1}}, "df": 5}}}}}}}}}}}}}}}}}}}}}}}}, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "k": {"docs": {"vertax.EdgePlot.BLACK": {"tf": 1}, "vertax.VertexPlot.BLACK": {"tf": 1}}, "df": 2}}}}}, "d": {"docs": {"vertax.cost_d_IAS": {"tf": 1}}, "df": 1, "i": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"vertax.BilevelOptimizationMethod.AUTOMATIC_DIFFERENTIATION": {"tf": 1}, "vertax.BilevelOptimizationMethod.IMPLICIT_DIFFERENTIATION": {"tf": 1}}, "df": 2}}}}}}}}}}}}}}}}}, "annotation": {"root": {"docs": {"vertax.Mesh.faces": {"tf": 1}, "vertax.Mesh.edges": {"tf": 1}, "vertax.Mesh.vertices": {"tf": 1}, "vertax.Mesh.width": {"tf": 1}, "vertax.Mesh.height": {"tf": 1}, "vertax.Mesh.faces_params": {"tf": 1}, "vertax.Mesh.edges_params": {"tf": 1}, "vertax.Mesh.vertices_params": {"tf": 1}, "vertax.Mesh.nb_faces": {"tf": 1}, "vertax.Mesh.nb_edges": {"tf": 1}, "vertax.Mesh.nb_half_edges": {"tf": 1}, "vertax.Mesh.nb_vertices": {"tf": 1}, "vertax.PbcMesh.MAX_EDGES_IN_ANY_FACE": {"tf": 1}, "vertax.BoundedMesh.angles": {"tf": 1}, "vertax.BoundedMesh.nb_angles": {"tf": 1}}, "df": 15, "j": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "x": {"docs": {"vertax.Mesh.faces": {"tf": 1}, "vertax.Mesh.edges": {"tf": 1}, "vertax.Mesh.vertices": {"tf": 1}, "vertax.Mesh.faces_params": {"tf": 1}, "vertax.Mesh.edges_params": {"tf": 1}, "vertax.Mesh.vertices_params": {"tf": 1}, "vertax.BoundedMesh.angles": {"tf": 1}}, "df": 7}}}, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "y": {"docs": {"vertax.Mesh.faces": {"tf": 1}, "vertax.Mesh.edges": {"tf": 1}, "vertax.Mesh.vertices": {"tf": 1}, "vertax.Mesh.faces_params": {"tf": 1}, "vertax.Mesh.edges_params": {"tf": 1}, "vertax.Mesh.vertices_params": {"tf": 1}, "vertax.BoundedMesh.angles": {"tf": 1}}, "df": 7}}}}}, "f": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {"vertax.Mesh.width": {"tf": 1}, "vertax.Mesh.height": {"tf": 1}}, "df": 2}}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"vertax.Mesh.nb_faces": {"tf": 1}, "vertax.Mesh.nb_edges": {"tf": 1}, "vertax.Mesh.nb_half_edges": {"tf": 1}, "vertax.Mesh.nb_vertices": {"tf": 1}, "vertax.PbcMesh.MAX_EDGES_IN_ANY_FACE": {"tf": 1}, "vertax.BoundedMesh.nb_angles": {"tf": 1}}, "df": 6}}}}}, "default_value": {"root": {"1": {"docs": {"vertax.FacePlot.MULTICOLOR": {"tf": 1}, "vertax.EdgePlot.BLACK": {"tf": 1}, "vertax.VertexPlot.BLACK": {"tf": 1}}, "df": 3}, "2": {"docs": {"vertax.FacePlot.FACE_PARAMETER": {"tf": 1}, "vertax.EdgePlot.EDGE_PARAMETER": {"tf": 1}, "vertax.VertexPlot.VERTEX_PARAMETER": {"tf": 1}}, "df": 3}, "3": {"docs": {"vertax.FacePlot.AREA": {"tf": 1}, "vertax.EdgePlot.LENGTH": {"tf": 1}, "vertax.VertexPlot.INVISIBLE": {"tf": 1}}, "df": 3}, "4": {"docs": {"vertax.FacePlot.PERIMETER": {"tf": 1}, "vertax.EdgePlot.INVISIBLE": {"tf": 1}}, "df": 2}, "5": {"docs": {"vertax.FacePlot.WHITE": {"tf": 1}}, "df": 1}, "6": {"docs": {"vertax.FacePlot.FATES": {"tf": 1}}, "df": 1}, "docs": {"vertax.BilevelOptimizationMethod.AUTOMATIC_DIFFERENTIATION": {"tf": 1.4142135623730951}, "vertax.BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION": {"tf": 1.4142135623730951}, "vertax.BilevelOptimizationMethod.IMPLICIT_DIFFERENTIATION": {"tf": 1.4142135623730951}, "vertax.BilevelOptimizationMethod.ADJOINT_STATE": {"tf": 1.4142135623730951}, "vertax.FacePlot.MULTICOLOR": {"tf": 1.4142135623730951}, "vertax.FacePlot.FACE_PARAMETER": {"tf": 1.4142135623730951}, "vertax.FacePlot.AREA": {"tf": 1.4142135623730951}, "vertax.FacePlot.PERIMETER": {"tf": 1.4142135623730951}, "vertax.FacePlot.WHITE": {"tf": 1.4142135623730951}, "vertax.FacePlot.FATES": {"tf": 1.4142135623730951}, "vertax.EdgePlot.BLACK": {"tf": 1.4142135623730951}, "vertax.EdgePlot.EDGE_PARAMETER": {"tf": 1.4142135623730951}, "vertax.EdgePlot.LENGTH": {"tf": 1.4142135623730951}, "vertax.EdgePlot.INVISIBLE": {"tf": 1.4142135623730951}, "vertax.VertexPlot.BLACK": {"tf": 1.4142135623730951}, "vertax.VertexPlot.VERTEX_PARAMETER": {"tf": 1.4142135623730951}, "vertax.VertexPlot.INVISIBLE": {"tf": 1.4142135623730951}}, "df": 17, "l": {"docs": {}, "df": 0, "t": {"docs": {"vertax.BilevelOptimizationMethod.AUTOMATIC_DIFFERENTIATION": {"tf": 1}, "vertax.BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION": {"tf": 1}, "vertax.BilevelOptimizationMethod.IMPLICIT_DIFFERENTIATION": {"tf": 1}, "vertax.BilevelOptimizationMethod.ADJOINT_STATE": {"tf": 1}, "vertax.FacePlot.MULTICOLOR": {"tf": 1}, "vertax.FacePlot.FACE_PARAMETER": {"tf": 1}, "vertax.FacePlot.AREA": {"tf": 1}, "vertax.FacePlot.PERIMETER": {"tf": 1}, "vertax.FacePlot.WHITE": {"tf": 1}, "vertax.FacePlot.FATES": {"tf": 1}, "vertax.EdgePlot.BLACK": {"tf": 1}, "vertax.EdgePlot.EDGE_PARAMETER": {"tf": 1}, "vertax.EdgePlot.LENGTH": {"tf": 1}, "vertax.EdgePlot.INVISIBLE": {"tf": 1}, "vertax.VertexPlot.BLACK": {"tf": 1}, "vertax.VertexPlot.VERTEX_PARAMETER": {"tf": 1}, "vertax.VertexPlot.INVISIBLE": {"tf": 1}}, "df": 17}, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"vertax.EdgePlot.LENGTH": {"tf": 1}}, "df": 1}}}}}}, "b": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "z": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {"vertax.BilevelOptimizationMethod.AUTOMATIC_DIFFERENTIATION": {"tf": 1}, "vertax.BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION": {"tf": 1}, "vertax.BilevelOptimizationMethod.IMPLICIT_DIFFERENTIATION": {"tf": 1}, "vertax.BilevelOptimizationMethod.ADJOINT_STATE": {"tf": 1}}, "df": 4}}}}}}}}}}}}}}}}}}}}}}}}, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "k": {"docs": {"vertax.EdgePlot.BLACK": {"tf": 1}, "vertax.VertexPlot.BLACK": {"tf": 1}}, "df": 2}}}}}, "a": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {"vertax.BilevelOptimizationMethod.AUTOMATIC_DIFFERENTIATION": {"tf": 1}}, "df": 1}}}}}}}}, "d": {"docs": {"vertax.BilevelOptimizationMethod.AUTOMATIC_DIFFERENTIATION": {"tf": 1}}, "df": 1, "j": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"vertax.BilevelOptimizationMethod.ADJOINT_STATE": {"tf": 1}}, "df": 1}}}}}}, "s": {"docs": {"vertax.BilevelOptimizationMethod.ADJOINT_STATE": {"tf": 1}}, "df": 1}, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {"vertax.FacePlot.AREA": {"tf": 1}}, "df": 1}}}}, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"vertax.BilevelOptimizationMethod.AUTOMATIC_DIFFERENTIATION": {"tf": 1}, "vertax.BilevelOptimizationMethod.IMPLICIT_DIFFERENTIATION": {"tf": 1}}, "df": 2}}}}}}}}}}}}}}}, "x": {"2": {"7": {"docs": {"vertax.BilevelOptimizationMethod.AUTOMATIC_DIFFERENTIATION": {"tf": 1.4142135623730951}, "vertax.BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION": {"tf": 1.4142135623730951}, "vertax.BilevelOptimizationMethod.IMPLICIT_DIFFERENTIATION": {"tf": 1.4142135623730951}, "vertax.BilevelOptimizationMethod.ADJOINT_STATE": {"tf": 1.4142135623730951}}, "df": 4}, "docs": {}, "df": 0}, "docs": {}, "df": 0}, "g": {"docs": {}, "df": 0, "t": {"docs": {"vertax.BilevelOptimizationMethod.AUTOMATIC_DIFFERENTIATION": {"tf": 1}, "vertax.BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION": {"tf": 1}, "vertax.BilevelOptimizationMethod.IMPLICIT_DIFFERENTIATION": {"tf": 1}, "vertax.BilevelOptimizationMethod.ADJOINT_STATE": {"tf": 1}, "vertax.FacePlot.MULTICOLOR": {"tf": 1}, "vertax.FacePlot.FACE_PARAMETER": {"tf": 1}, "vertax.FacePlot.AREA": {"tf": 1}, "vertax.FacePlot.PERIMETER": {"tf": 1}, "vertax.FacePlot.WHITE": {"tf": 1}, "vertax.FacePlot.FATES": {"tf": 1}, "vertax.EdgePlot.BLACK": {"tf": 1}, "vertax.EdgePlot.EDGE_PARAMETER": {"tf": 1}, "vertax.EdgePlot.LENGTH": {"tf": 1}, "vertax.EdgePlot.INVISIBLE": {"tf": 1}, "vertax.VertexPlot.BLACK": {"tf": 1}, "vertax.VertexPlot.VERTEX_PARAMETER": {"tf": 1}, "vertax.VertexPlot.INVISIBLE": {"tf": 1}}, "df": 17}}, "e": {"docs": {}, "df": 0, "q": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "m": {"docs": {"vertax.BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION": {"tf": 1}}, "df": 1}}}}}}}}}}, "p": {"docs": {"vertax.BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION": {"tf": 1}}, "df": 1}, "d": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {"vertax.EdgePlot.EDGE_PARAMETER": {"tf": 1}}, "df": 1, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "t": {"docs": {"vertax.EdgePlot.BLACK": {"tf": 1}, "vertax.EdgePlot.EDGE_PARAMETER": {"tf": 1}, "vertax.EdgePlot.LENGTH": {"tf": 1}, "vertax.EdgePlot.INVISIBLE": {"tf": 1}}, "df": 4}}}}}}}}, "p": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"vertax.BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION": {"tf": 1}}, "df": 1}}}}}}}}}}, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"vertax.FacePlot.FACE_PARAMETER": {"tf": 1}, "vertax.EdgePlot.EDGE_PARAMETER": {"tf": 1}, "vertax.VertexPlot.VERTEX_PARAMETER": {"tf": 1}}, "df": 3}}}}}}}}, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"vertax.FacePlot.PERIMETER": {"tf": 1}}, "df": 1}}}}}}}}}, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {"vertax.BilevelOptimizationMethod.IMPLICIT_DIFFERENTIATION": {"tf": 1}}, "df": 1}}}}}}}, "d": {"docs": {"vertax.BilevelOptimizationMethod.IMPLICIT_DIFFERENTIATION": {"tf": 1}}, "df": 1}, "n": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"vertax.EdgePlot.INVISIBLE": {"tf": 1}, "vertax.VertexPlot.INVISIBLE": {"tf": 1}}, "df": 2}}}}}}}}}, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"vertax.BilevelOptimizationMethod.ADJOINT_STATE": {"tf": 1}}, "df": 1}}}}}, "f": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {"vertax.FacePlot.FACE_PARAMETER": {"tf": 1}}, "df": 1, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "t": {"docs": {"vertax.FacePlot.MULTICOLOR": {"tf": 1}, "vertax.FacePlot.FACE_PARAMETER": {"tf": 1}, "vertax.FacePlot.AREA": {"tf": 1}, "vertax.FacePlot.PERIMETER": {"tf": 1}, "vertax.FacePlot.WHITE": {"tf": 1}, "vertax.FacePlot.FATES": {"tf": 1}}, "df": 6}}}}}}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"vertax.FacePlot.FATES": {"tf": 1}}, "df": 1}}}}}, "m": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {"vertax.FacePlot.MULTICOLOR": {"tf": 1}}, "df": 1}}}}}}}}}}, "w": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"vertax.FacePlot.WHITE": {"tf": 1}}, "df": 1}}}}}, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "x": {"docs": {"vertax.VertexPlot.VERTEX_PARAMETER": {"tf": 1}}, "df": 1, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "t": {"docs": {"vertax.VertexPlot.BLACK": {"tf": 1}, "vertax.VertexPlot.VERTEX_PARAMETER": {"tf": 1}, "vertax.VertexPlot.INVISIBLE": {"tf": 1}}, "df": 3}}}}}}}}}}}}, "signature": {"root": {"1": {"docs": {"vertax.plot_mesh": {"tf": 1.4142135623730951}, "vertax.get_plot_mesh": {"tf": 1.4142135623730951}}, "df": 2}, "2": {"0": {"docs": {"vertax.plot_mesh": {"tf": 1}, "vertax.get_plot_mesh": {"tf": 1}}, "df": 2}, "docs": {"vertax.BoundedMesh.from_random_seeds": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}, "vertax.plot_mesh": {"tf": 1}, "vertax.get_plot_mesh": {"tf": 1}}, "df": 4}, "3": {"9": {"docs": {"vertax.PbcMesh.save_mesh_txt": {"tf": 3.7416573867739413}, "vertax.PbcMesh.load_mesh_txt": {"tf": 3.7416573867739413}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 4}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 4}, "vertax.plot_mesh": {"tf": 4}, "vertax.get_plot_mesh": {"tf": 3.7416573867739413}}, "df": 6}, "docs": {"vertax.plot_mesh": {"tf": 1}, "vertax.get_plot_mesh": {"tf": 1}}, "df": 2}, "docs": {"vertax.Mesh.__init__": {"tf": 2}, "vertax.Mesh.save_mesh": {"tf": 4.47213595499958}, "vertax.Mesh.load_mesh": {"tf": 4.47213595499958}, "vertax.PbcMesh.__init__": {"tf": 2}, "vertax.PbcMesh.from_random_seeds": {"tf": 6.6332495807108}, "vertax.PbcMesh.from_seeds": {"tf": 6.324555320336759}, "vertax.PbcMesh.from_image": {"tf": 7.0710678118654755}, "vertax.PbcMesh.from_mask": {"tf": 7.0710678118654755}, "vertax.PbcMesh.create_empty": {"tf": 3.4641016151377544}, "vertax.PbcMesh.copy_mesh": {"tf": 4.47213595499958}, "vertax.PbcMesh.save_mesh": {"tf": 4.47213595499958}, "vertax.PbcMesh.load_mesh": {"tf": 4.47213595499958}, "vertax.PbcMesh.save_mesh_txt": {"tf": 11.874342087037917}, "vertax.PbcMesh.load_mesh_txt": {"tf": 11.874342087037917}, "vertax.PbcMesh.get_length": {"tf": 5.291502622129181}, "vertax.PbcMesh.get_length_with_offset": {"tf": 5.291502622129181}, "vertax.PbcMesh.get_perimeter": {"tf": 5.291502622129181}, "vertax.PbcMesh.get_area": {"tf": 5.291502622129181}, "vertax.PbcMesh.update_boundary_conditions": {"tf": 3.4641016151377544}, "vertax.BoundedMesh.__init__": {"tf": 2}, "vertax.BoundedMesh.from_random_seeds": {"tf": 8}, "vertax.BoundedMesh.from_seeds": {"tf": 9.591663046625438}, "vertax.BoundedMesh.create_empty": {"tf": 3.4641016151377544}, "vertax.BoundedMesh.copy_mesh": {"tf": 4.47213595499958}, "vertax.BoundedMesh.save_mesh": {"tf": 4.47213595499958}, "vertax.BoundedMesh.load_mesh": {"tf": 4.47213595499958}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 12.569805089976535}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 12.569805089976535}, "vertax.BoundedMesh.get_length": {"tf": 5.291502622129181}, "vertax.BoundedMesh.get_perimeter": {"tf": 5.291502622129181}, "vertax.BoundedMesh.get_area": {"tf": 5.291502622129181}, "vertax.PbcBilevelOptimizer.__init__": {"tf": 2}, "vertax.PbcBilevelOptimizer.compute_outer_loss": {"tf": 10.44030650891055}, "vertax.PbcBilevelOptimizer.compute_inner_loss": {"tf": 10.44030650891055}, "vertax.BoundedBilevelOptimizer.__init__": {"tf": 2}, "vertax.BoundedBilevelOptimizer.compute_outer_loss": {"tf": 10.44030650891055}, "vertax.BoundedBilevelOptimizer.compute_inner_loss": {"tf": 10.44030650891055}, "vertax.plot_mesh": {"tf": 20.92844953645635}, "vertax.get_plot_mesh": {"tf": 20.54263858417414}, "vertax.cost_v2v": {"tf": 14.38749456993816}, "vertax.cost_mesh2image": {"tf": 12.884098726725126}, "vertax.cost_areas": {"tf": 12.767145334803704}, "vertax.cost_IAS": {"tf": 14.491376746189438}, "vertax.cost_d_IAS": {"tf": 14.422205101855956}, "vertax.cost_tem_halfedge": {"tf": 14.422205101855956}, "vertax.cost_v2v_ias": {"tf": 14.422205101855956}, "vertax.cost_v2v_tem": {"tf": 14.422205101855956}, "vertax.cost_ratio": {"tf": 17.320508075688775}, "vertax.cost_checkerboard": {"tf": 16.278820596099706}, "vertax.energy_shape_factor_hetero": {"tf": 9.746794344808963}, "vertax.energy_shape_factor_homo": {"tf": 9.055385138137417}, "vertax.energy_line_tensions": {"tf": 9.746794344808963}, "vertax.energy_line_tensions_bounded": {"tf": 12.727922061357855}}, "df": 53, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "f": {"docs": {"vertax.Mesh.save_mesh": {"tf": 1}, "vertax.Mesh.load_mesh": {"tf": 1}, "vertax.PbcMesh.from_random_seeds": {"tf": 1}, "vertax.PbcMesh.from_seeds": {"tf": 1}, "vertax.PbcMesh.from_image": {"tf": 1}, "vertax.PbcMesh.from_mask": {"tf": 1}, "vertax.PbcMesh.create_empty": {"tf": 1}, "vertax.PbcMesh.copy_mesh": {"tf": 1.4142135623730951}, "vertax.PbcMesh.save_mesh": {"tf": 1}, "vertax.PbcMesh.load_mesh": {"tf": 1}, "vertax.PbcMesh.save_mesh_txt": {"tf": 1}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1}, "vertax.PbcMesh.get_length": {"tf": 1}, "vertax.PbcMesh.get_length_with_offset": {"tf": 1}, "vertax.PbcMesh.get_perimeter": {"tf": 1}, "vertax.PbcMesh.get_area": {"tf": 1}, "vertax.PbcMesh.update_boundary_conditions": {"tf": 1}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}, "vertax.BoundedMesh.create_empty": {"tf": 1}, "vertax.BoundedMesh.copy_mesh": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.save_mesh": {"tf": 1}, "vertax.BoundedMesh.load_mesh": {"tf": 1}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.get_length": {"tf": 1}, "vertax.BoundedMesh.get_perimeter": {"tf": 1}, "vertax.BoundedMesh.get_area": {"tf": 1}, "vertax.PbcBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.PbcBilevelOptimizer.compute_inner_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_inner_loss": {"tf": 1}}, "df": 32}, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"vertax.cost_v2v": {"tf": 1.7320508075688772}, "vertax.cost_mesh2image": {"tf": 1.7320508075688772}, "vertax.cost_areas": {"tf": 1.7320508075688772}, "vertax.cost_IAS": {"tf": 1.7320508075688772}, "vertax.cost_d_IAS": {"tf": 1.7320508075688772}, "vertax.cost_tem_halfedge": {"tf": 1.7320508075688772}, "vertax.cost_v2v_ias": {"tf": 1.7320508075688772}, "vertax.cost_v2v_tem": {"tf": 1.7320508075688772}, "vertax.cost_ratio": {"tf": 1.7320508075688772}, "vertax.cost_checkerboard": {"tf": 1.7320508075688772}, "vertax.energy_shape_factor_hetero": {"tf": 1}, "vertax.energy_line_tensions_bounded": {"tf": 1.7320508075688772}}, "df": 12}}}}}}, "e": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "s": {"docs": {"vertax.PbcMesh.from_random_seeds": {"tf": 1}, "vertax.PbcMesh.from_seeds": {"tf": 1}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}}, "df": 4}}}}, "t": {"docs": {}, "df": 0, "r": {"docs": {"vertax.Mesh.save_mesh": {"tf": 1}, "vertax.Mesh.load_mesh": {"tf": 1}, "vertax.PbcMesh.save_mesh": {"tf": 1}, "vertax.PbcMesh.load_mesh": {"tf": 1}, "vertax.PbcMesh.save_mesh_txt": {"tf": 2.8284271247461903}, "vertax.PbcMesh.load_mesh_txt": {"tf": 2.8284271247461903}, "vertax.BoundedMesh.save_mesh": {"tf": 1}, "vertax.BoundedMesh.load_mesh": {"tf": 1}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 3}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 3}, "vertax.plot_mesh": {"tf": 2.8284271247461903}, "vertax.get_plot_mesh": {"tf": 2.6457513110645907}}, "df": 12}}, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {"vertax.PbcMesh.from_image": {"tf": 1}, "vertax.PbcMesh.from_mask": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}}, "df": 3}}}}}}, "e": {"docs": {"vertax.plot_mesh": {"tf": 1.7320508075688772}, "vertax.get_plot_mesh": {"tf": 1.7320508075688772}}, "df": 2}}}}, "h": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "w": {"docs": {"vertax.plot_mesh": {"tf": 1}}, "df": 1}}}, "a": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {"vertax.plot_mesh": {"tf": 1.4142135623730951}}, "df": 1}}}, "p": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"vertax.plot_mesh": {"tf": 1}, "vertax.get_plot_mesh": {"tf": 1}}, "df": 2}}}}}, "i": {"docs": {}, "df": 0, "z": {"docs": {}, "df": 0, "e": {"docs": {"vertax.plot_mesh": {"tf": 1}, "vertax.get_plot_mesh": {"tf": 1}}, "df": 2}}}}, "p": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"vertax.Mesh.save_mesh": {"tf": 1}, "vertax.Mesh.load_mesh": {"tf": 1}, "vertax.PbcMesh.save_mesh": {"tf": 1}, "vertax.PbcMesh.load_mesh": {"tf": 1}, "vertax.BoundedMesh.save_mesh": {"tf": 1}, "vertax.BoundedMesh.load_mesh": {"tf": 1}, "vertax.plot_mesh": {"tf": 1}}, "df": 7}}, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "s": {"docs": {"vertax.PbcMesh.save_mesh_txt": {"tf": 1.7320508075688772}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1.7320508075688772}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1.7320508075688772}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1.7320508075688772}, "vertax.energy_shape_factor_hetero": {"tf": 1}, "vertax.energy_shape_factor_homo": {"tf": 1}, "vertax.energy_line_tensions": {"tf": 1.4142135623730951}, "vertax.energy_line_tensions_bounded": {"tf": 1.7320508075688772}}, "df": 8}, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {"vertax.plot_mesh": {"tf": 1.7320508075688772}, "vertax.get_plot_mesh": {"tf": 1.7320508075688772}}, "df": 2}}}}}}}}}, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "t": {"docs": {"vertax.plot_mesh": {"tf": 2.449489742783178}, "vertax.get_plot_mesh": {"tf": 2.449489742783178}}, "df": 2}}}, "b": {"docs": {}, "df": 0, "c": {"docs": {"vertax.plot_mesh": {"tf": 1}}, "df": 1}}, "n": {"docs": {}, "df": 0, "g": {"docs": {"vertax.plot_mesh": {"tf": 1}}, "df": 1}}}, "n": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {"vertax.Mesh.save_mesh": {"tf": 1}, "vertax.PbcMesh.save_mesh": {"tf": 1}, "vertax.PbcMesh.save_mesh_txt": {"tf": 1}, "vertax.PbcMesh.update_boundary_conditions": {"tf": 1}, "vertax.BoundedMesh.save_mesh": {"tf": 1}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1}, "vertax.PbcBilevelOptimizer.compute_outer_loss": {"tf": 2.449489742783178}, "vertax.PbcBilevelOptimizer.compute_inner_loss": {"tf": 2.449489742783178}, "vertax.BoundedBilevelOptimizer.compute_outer_loss": {"tf": 2.449489742783178}, "vertax.BoundedBilevelOptimizer.compute_inner_loss": {"tf": 2.449489742783178}, "vertax.plot_mesh": {"tf": 2.6457513110645907}, "vertax.get_plot_mesh": {"tf": 2.449489742783178}, "vertax.cost_v2v": {"tf": 2.8284271247461903}, "vertax.cost_IAS": {"tf": 2.8284271247461903}, "vertax.cost_d_IAS": {"tf": 2.8284271247461903}, "vertax.cost_tem_halfedge": {"tf": 2.8284271247461903}, "vertax.cost_v2v_ias": {"tf": 2.8284271247461903}, "vertax.cost_v2v_tem": {"tf": 2.8284271247461903}, "vertax.cost_ratio": {"tf": 4.69041575982343}, "vertax.cost_checkerboard": {"tf": 4}, "vertax.energy_line_tensions_bounded": {"tf": 1.7320508075688772}}, "df": 21}}}, "b": {"docs": {"vertax.PbcMesh.from_random_seeds": {"tf": 1}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.from_seeds": {"tf": 1}}, "df": 3}, "u": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "y": {"docs": {"vertax.PbcMesh.from_image": {"tf": 1.4142135623730951}, "vertax.PbcMesh.from_mask": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.from_seeds": {"tf": 1.4142135623730951}}, "df": 3}}}}, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "y": {"docs": {"vertax.PbcMesh.from_image": {"tf": 1}, "vertax.PbcMesh.from_mask": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}}, "df": 3}}}}}}, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"vertax.plot_mesh": {"tf": 2.449489742783178}, "vertax.get_plot_mesh": {"tf": 2.449489742783178}}, "df": 2}}}}, "c": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "s": {"docs": {"vertax.Mesh.load_mesh": {"tf": 1}, "vertax.PbcMesh.from_random_seeds": {"tf": 1}, "vertax.PbcMesh.from_seeds": {"tf": 1}, "vertax.PbcMesh.from_image": {"tf": 1}, "vertax.PbcMesh.from_mask": {"tf": 1}, "vertax.PbcMesh.create_empty": {"tf": 1}, "vertax.PbcMesh.copy_mesh": {"tf": 1}, "vertax.PbcMesh.load_mesh": {"tf": 1}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}, "vertax.BoundedMesh.create_empty": {"tf": 1}, "vertax.BoundedMesh.copy_mesh": {"tf": 1}, "vertax.BoundedMesh.load_mesh": {"tf": 1}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1}}, "df": 15}}, "o": {"docs": {"vertax.PbcMesh.from_image": {"tf": 1}, "vertax.PbcMesh.from_mask": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}}, "df": 3, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "s": {"docs": {"vertax.PbcMesh.save_mesh_txt": {"tf": 1.4142135623730951}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1.4142135623730951}}, "df": 4}}}}}}}, "o": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "m": {"docs": {"vertax.plot_mesh": {"tf": 1}, "vertax.get_plot_mesh": {"tf": 1}}, "df": 2}}}}}}}, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "p": {"docs": {"vertax.plot_mesh": {"tf": 1.7320508075688772}, "vertax.get_plot_mesh": {"tf": 1.7320508075688772}}, "df": 2}}}, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {"vertax.plot_mesh": {"tf": 1}, "vertax.get_plot_mesh": {"tf": 1}}, "df": 2}}}}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"vertax.PbcMesh.from_random_seeds": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1.7320508075688772}, "vertax.BoundedMesh.from_seeds": {"tf": 1.4142135623730951}, "vertax.PbcBilevelOptimizer.compute_outer_loss": {"tf": 1.7320508075688772}, "vertax.PbcBilevelOptimizer.compute_inner_loss": {"tf": 1.7320508075688772}, "vertax.BoundedBilevelOptimizer.compute_outer_loss": {"tf": 1.7320508075688772}, "vertax.BoundedBilevelOptimizer.compute_inner_loss": {"tf": 1.7320508075688772}, "vertax.cost_d_IAS": {"tf": 1}}, "df": 8}, "v": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"vertax.plot_mesh": {"tf": 1}, "vertax.get_plot_mesh": {"tf": 1}}, "df": 2}}}}}}}}, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {"vertax.PbcMesh.from_image": {"tf": 1}, "vertax.cost_v2v": {"tf": 1}, "vertax.cost_mesh2image": {"tf": 1}, "vertax.cost_areas": {"tf": 1}, "vertax.cost_IAS": {"tf": 1}, "vertax.cost_d_IAS": {"tf": 1}, "vertax.cost_tem_halfedge": {"tf": 1}, "vertax.cost_v2v_ias": {"tf": 1}, "vertax.cost_v2v_tem": {"tf": 1}, "vertax.cost_ratio": {"tf": 1}, "vertax.cost_checkerboard": {"tf": 1}}, "df": 11}}}}, "d": {"docs": {"vertax.PbcMesh.get_length": {"tf": 1}, "vertax.PbcMesh.get_length_with_offset": {"tf": 1}, "vertax.PbcMesh.get_perimeter": {"tf": 1}, "vertax.PbcMesh.get_area": {"tf": 1}, "vertax.BoundedMesh.get_length": {"tf": 1}, "vertax.BoundedMesh.get_perimeter": {"tf": 1}, "vertax.BoundedMesh.get_area": {"tf": 1}}, "df": 7}}, "w": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"vertax.PbcMesh.from_random_seeds": {"tf": 1}, "vertax.PbcMesh.from_seeds": {"tf": 1}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}, "vertax.plot_mesh": {"tf": 1}, "vertax.get_plot_mesh": {"tf": 1}, "vertax.cost_v2v": {"tf": 1}, "vertax.cost_mesh2image": {"tf": 1}, "vertax.cost_areas": {"tf": 1}, "vertax.cost_IAS": {"tf": 1}, "vertax.cost_d_IAS": {"tf": 1}, "vertax.cost_tem_halfedge": {"tf": 1}, "vertax.cost_v2v_ias": {"tf": 1}, "vertax.cost_v2v_tem": {"tf": 1}, "vertax.energy_shape_factor_hetero": {"tf": 1}, "vertax.energy_shape_factor_homo": {"tf": 1}, "vertax.energy_line_tensions": {"tf": 1}}, "df": 17}}}}}, "f": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {"vertax.PbcMesh.from_random_seeds": {"tf": 1.4142135623730951}, "vertax.PbcMesh.from_seeds": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.from_seeds": {"tf": 1.4142135623730951}, "vertax.PbcBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.PbcBilevelOptimizer.compute_inner_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_inner_loss": {"tf": 1}, "vertax.plot_mesh": {"tf": 2.8284271247461903}, "vertax.get_plot_mesh": {"tf": 2.8284271247461903}, "vertax.cost_v2v": {"tf": 1.4142135623730951}, "vertax.cost_mesh2image": {"tf": 1.4142135623730951}, "vertax.cost_areas": {"tf": 1.4142135623730951}, "vertax.cost_IAS": {"tf": 1.4142135623730951}, "vertax.cost_d_IAS": {"tf": 1.4142135623730951}, "vertax.cost_tem_halfedge": {"tf": 1.4142135623730951}, "vertax.cost_v2v_ias": {"tf": 1.4142135623730951}, "vertax.cost_v2v_tem": {"tf": 1.4142135623730951}, "vertax.energy_shape_factor_hetero": {"tf": 1.4142135623730951}, "vertax.energy_shape_factor_homo": {"tf": 1.4142135623730951}, "vertax.energy_line_tensions": {"tf": 1.4142135623730951}}, "df": 21}}}}, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"vertax.PbcMesh.save_mesh_txt": {"tf": 2.6457513110645907}, "vertax.PbcMesh.load_mesh_txt": {"tf": 2.6457513110645907}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 2.8284271247461903}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 2.8284271247461903}}, "df": 4}}}}}}, "g": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"vertax.get_plot_mesh": {"tf": 1.4142135623730951}}, "df": 1}}}}}, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {"vertax.PbcMesh.get_perimeter": {"tf": 1}, "vertax.PbcMesh.get_area": {"tf": 1}, "vertax.BoundedMesh.get_perimeter": {"tf": 1}, "vertax.BoundedMesh.get_area": {"tf": 1}, "vertax.plot_mesh": {"tf": 1.7320508075688772}, "vertax.get_plot_mesh": {"tf": 1.7320508075688772}, "vertax.energy_shape_factor_hetero": {"tf": 1}, "vertax.energy_shape_factor_homo": {"tf": 1}, "vertax.energy_line_tensions": {"tf": 1}, "vertax.energy_line_tensions_bounded": {"tf": 1}}, "df": 10, "s": {"docs": {"vertax.PbcMesh.save_mesh_txt": {"tf": 1.4142135623730951}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1.4142135623730951}, "vertax.PbcBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.PbcBilevelOptimizer.compute_inner_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_inner_loss": {"tf": 1}, "vertax.plot_mesh": {"tf": 1}, "vertax.get_plot_mesh": {"tf": 1}, "vertax.cost_v2v": {"tf": 1}, "vertax.cost_mesh2image": {"tf": 1}, "vertax.cost_areas": {"tf": 1}, "vertax.cost_IAS": {"tf": 1}, "vertax.cost_d_IAS": {"tf": 1}, "vertax.cost_tem_halfedge": {"tf": 1}, "vertax.cost_v2v_ias": {"tf": 1}, "vertax.cost_v2v_tem": {"tf": 1}, "vertax.cost_ratio": {"tf": 1}, "vertax.cost_checkerboard": {"tf": 1}, "vertax.energy_shape_factor_hetero": {"tf": 1}, "vertax.energy_line_tensions_bounded": {"tf": 1}}, "df": 22}, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"vertax.PbcMesh.save_mesh_txt": {"tf": 1}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1}, "vertax.cost_v2v": {"tf": 1.4142135623730951}, "vertax.cost_mesh2image": {"tf": 1.4142135623730951}, "vertax.cost_areas": {"tf": 1.4142135623730951}, "vertax.cost_IAS": {"tf": 1.4142135623730951}, "vertax.cost_d_IAS": {"tf": 1.4142135623730951}, "vertax.cost_tem_halfedge": {"tf": 1.4142135623730951}, "vertax.cost_v2v_ias": {"tf": 1.4142135623730951}, "vertax.cost_v2v_tem": {"tf": 1.4142135623730951}, "vertax.cost_ratio": {"tf": 1.4142135623730951}, "vertax.cost_checkerboard": {"tf": 1.4142135623730951}, "vertax.energy_shape_factor_hetero": {"tf": 1}, "vertax.energy_shape_factor_homo": {"tf": 1}, "vertax.energy_line_tensions": {"tf": 1}, "vertax.energy_line_tensions_bounded": {"tf": 1}}, "df": 18}}}}}, "p": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"vertax.PbcMesh.save_mesh_txt": {"tf": 1}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1}}, "df": 4}}}}}}}}}}, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "t": {"docs": {"vertax.plot_mesh": {"tf": 1.4142135623730951}, "vertax.get_plot_mesh": {"tf": 1.4142135623730951}}, "df": 2}}}}}}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"vertax.BoundedMesh.from_random_seeds": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}}, "df": 2}}}, "l": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"vertax.plot_mesh": {"tf": 1}}, "df": 1}}}}, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"vertax.plot_mesh": {"tf": 1.7320508075688772}, "vertax.get_plot_mesh": {"tf": 1.7320508075688772}}, "df": 2}}}}}}, "h": {"docs": {}, "df": 0, "e": {"docs": {"vertax.energy_line_tensions": {"tf": 1}, "vertax.energy_line_tensions_bounded": {"tf": 1}}, "df": 2, "i": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "t": {"docs": {"vertax.PbcMesh.from_random_seeds": {"tf": 1}, "vertax.PbcMesh.from_seeds": {"tf": 1}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}, "vertax.cost_v2v": {"tf": 1}, "vertax.cost_mesh2image": {"tf": 1}, "vertax.cost_areas": {"tf": 1}, "vertax.cost_IAS": {"tf": 1}, "vertax.cost_d_IAS": {"tf": 1}, "vertax.cost_tem_halfedge": {"tf": 1}, "vertax.cost_v2v_ias": {"tf": 1}, "vertax.cost_v2v_tem": {"tf": 1}, "vertax.energy_shape_factor_hetero": {"tf": 1}, "vertax.energy_shape_factor_homo": {"tf": 1}, "vertax.energy_line_tensions": {"tf": 1}}, "df": 15}}}}, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"vertax.PbcMesh.save_mesh_txt": {"tf": 1}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1}, "vertax.cost_v2v": {"tf": 1.4142135623730951}, "vertax.cost_mesh2image": {"tf": 1.4142135623730951}, "vertax.cost_areas": {"tf": 1.4142135623730951}, "vertax.cost_IAS": {"tf": 1.4142135623730951}, "vertax.cost_d_IAS": {"tf": 1.4142135623730951}, "vertax.cost_tem_halfedge": {"tf": 1.4142135623730951}, "vertax.cost_v2v_ias": {"tf": 1.4142135623730951}, "vertax.cost_v2v_tem": {"tf": 1.4142135623730951}, "vertax.cost_ratio": {"tf": 1.4142135623730951}, "vertax.cost_checkerboard": {"tf": 1.4142135623730951}, "vertax.energy_shape_factor_hetero": {"tf": 1}, "vertax.energy_shape_factor_homo": {"tf": 1}, "vertax.energy_line_tensions": {"tf": 1}, "vertax.energy_line_tensions_bounded": {"tf": 1}}, "df": 18}}}}}, "p": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"vertax.PbcMesh.save_mesh_txt": {"tf": 1}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1}}, "df": 4}}}}}}}}}}}, "s": {"docs": {"vertax.cost_v2v": {"tf": 1}, "vertax.cost_mesh2image": {"tf": 1}, "vertax.cost_areas": {"tf": 1}, "vertax.cost_IAS": {"tf": 1}, "vertax.cost_d_IAS": {"tf": 1}, "vertax.cost_tem_halfedge": {"tf": 1}, "vertax.cost_v2v_ias": {"tf": 1}, "vertax.cost_v2v_tem": {"tf": 1}, "vertax.cost_ratio": {"tf": 1}, "vertax.cost_checkerboard": {"tf": 1}, "vertax.energy_line_tensions_bounded": {"tf": 1}}, "df": 11}}, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "f": {"docs": {"vertax.PbcMesh.get_length": {"tf": 1}, "vertax.PbcMesh.get_length_with_offset": {"tf": 1}, "vertax.BoundedMesh.get_length": {"tf": 1}}, "df": 3}}}}, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "m": {"docs": {"vertax.PbcMesh.from_random_seeds": {"tf": 1}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}}, "df": 3}}}}}}, "k": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "y": {"docs": {"vertax.PbcMesh.from_random_seeds": {"tf": 1}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}}, "df": 3}}}, "j": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "x": {"docs": {"vertax.PbcMesh.from_seeds": {"tf": 1}, "vertax.PbcMesh.get_length": {"tf": 1.4142135623730951}, "vertax.PbcMesh.get_length_with_offset": {"tf": 1.4142135623730951}, "vertax.PbcMesh.get_perimeter": {"tf": 1.4142135623730951}, "vertax.PbcMesh.get_area": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.get_length": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.get_perimeter": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.get_area": {"tf": 1.4142135623730951}, "vertax.cost_v2v": {"tf": 3.3166247903554}, "vertax.cost_mesh2image": {"tf": 3.3166247903554}, "vertax.cost_areas": {"tf": 3.3166247903554}, "vertax.cost_IAS": {"tf": 3.3166247903554}, "vertax.cost_d_IAS": {"tf": 3.1622776601683795}, "vertax.cost_tem_halfedge": {"tf": 3.3166247903554}, "vertax.cost_v2v_ias": {"tf": 3.3166247903554}, "vertax.cost_v2v_tem": {"tf": 3.3166247903554}, "vertax.cost_ratio": {"tf": 3.605551275463989}, "vertax.cost_checkerboard": {"tf": 3.605551275463989}, "vertax.energy_shape_factor_hetero": {"tf": 2.449489742783178}, "vertax.energy_shape_factor_homo": {"tf": 2.23606797749979}, "vertax.energy_line_tensions": {"tf": 2.449489742783178}, "vertax.energy_line_tensions_bounded": {"tf": 3.3166247903554}}, "df": 22}}}, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "y": {"docs": {"vertax.PbcMesh.from_seeds": {"tf": 1}, "vertax.PbcMesh.get_length": {"tf": 1.4142135623730951}, "vertax.PbcMesh.get_length_with_offset": {"tf": 1.4142135623730951}, "vertax.PbcMesh.get_perimeter": {"tf": 1.4142135623730951}, "vertax.PbcMesh.get_area": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.get_length": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.get_perimeter": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.get_area": {"tf": 1.4142135623730951}, "vertax.cost_v2v": {"tf": 3.3166247903554}, "vertax.cost_mesh2image": {"tf": 3.3166247903554}, "vertax.cost_areas": {"tf": 3.3166247903554}, "vertax.cost_IAS": {"tf": 3.3166247903554}, "vertax.cost_d_IAS": {"tf": 3.1622776601683795}, "vertax.cost_tem_halfedge": {"tf": 3.3166247903554}, "vertax.cost_v2v_ias": {"tf": 3.3166247903554}, "vertax.cost_v2v_tem": {"tf": 3.3166247903554}, "vertax.cost_ratio": {"tf": 3.605551275463989}, "vertax.cost_checkerboard": {"tf": 3.605551275463989}, "vertax.energy_shape_factor_hetero": {"tf": 2.449489742783178}, "vertax.energy_shape_factor_homo": {"tf": 2.23606797749979}, "vertax.energy_line_tensions": {"tf": 2.449489742783178}, "vertax.energy_line_tensions_bounded": {"tf": 3.3166247903554}}, "df": 22}}}}, "n": {"docs": {}, "df": 0, "y": {"docs": {"vertax.PbcMesh.from_image": {"tf": 1}, "vertax.PbcMesh.from_mask": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}}, "df": 3}, "g": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"vertax.BoundedMesh.save_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1}}, "df": 2}}}, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"vertax.BoundedMesh.save_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1}, "vertax.cost_ratio": {"tf": 1.4142135623730951}, "vertax.cost_checkerboard": {"tf": 1.4142135623730951}, "vertax.energy_line_tensions_bounded": {"tf": 1}}, "df": 5}}}}}}}, "x": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"vertax.get_plot_mesh": {"tf": 1.7320508075688772}}, "df": 1}}}}, "t": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"vertax.PbcMesh.from_image": {"tf": 1}, "vertax.PbcMesh.from_mask": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}}, "df": 3}}}}}, "x": {"docs": {}, "df": 0, "t": {"docs": {"vertax.PbcMesh.save_mesh_txt": {"tf": 2.6457513110645907}, "vertax.PbcMesh.load_mesh_txt": {"tf": 2.6457513110645907}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 2.8284271247461903}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 2.8284271247461903}}, "df": 4}}, "r": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "e": {"docs": {"vertax.plot_mesh": {"tf": 1}}, "df": 1}}}, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"vertax.plot_mesh": {"tf": 1}, "vertax.get_plot_mesh": {"tf": 1}}, "df": 2}}}}, "u": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"vertax.plot_mesh": {"tf": 1.7320508075688772}, "vertax.get_plot_mesh": {"tf": 2}}, "df": 2}}}}, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {"vertax.cost_v2v": {"tf": 2}, "vertax.cost_mesh2image": {"tf": 2}, "vertax.cost_areas": {"tf": 2}, "vertax.cost_IAS": {"tf": 2}, "vertax.cost_d_IAS": {"tf": 2}, "vertax.cost_tem_halfedge": {"tf": 2}, "vertax.cost_v2v_ias": {"tf": 2}, "vertax.cost_v2v_tem": {"tf": 2}, "vertax.cost_ratio": {"tf": 2.23606797749979}, "vertax.cost_checkerboard": {"tf": 2.23606797749979}}, "df": 10}}}}}}, "d": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {"vertax.PbcMesh.from_image": {"tf": 1}, "vertax.PbcMesh.from_mask": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}}, "df": 3}}}}, "i": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "y": {"docs": {"vertax.PbcMesh.save_mesh_txt": {"tf": 1}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1}}, "df": 4}}}}}}}}}, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "k": {"docs": {"vertax.PbcMesh.from_mask": {"tf": 1}}, "df": 1}}, "t": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "b": {"docs": {"vertax.get_plot_mesh": {"tf": 1.4142135623730951}}, "df": 1}}}}}}}}}, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "h": {"docs": {"vertax.PbcMesh.copy_mesh": {"tf": 1}, "vertax.BoundedMesh.copy_mesh": {"tf": 1}, "vertax.PbcBilevelOptimizer.compute_outer_loss": {"tf": 1.7320508075688772}, "vertax.PbcBilevelOptimizer.compute_inner_loss": {"tf": 1.7320508075688772}, "vertax.BoundedBilevelOptimizer.compute_outer_loss": {"tf": 1.7320508075688772}, "vertax.BoundedBilevelOptimizer.compute_inner_loss": {"tf": 1.7320508075688772}, "vertax.plot_mesh": {"tf": 2}, "vertax.get_plot_mesh": {"tf": 1.7320508075688772}}, "df": 8, "e": {"docs": {}, "df": 0, "s": {"docs": {"vertax.PbcBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.PbcBilevelOptimizer.compute_inner_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_inner_loss": {"tf": 1}, "vertax.plot_mesh": {"tf": 2}, "vertax.get_plot_mesh": {"tf": 2}}, "df": 6}}}}}, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {"vertax.plot_mesh": {"tf": 1}, "vertax.get_plot_mesh": {"tf": 1}}, "df": 2}}}}}}}}}}, "o": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"vertax.PbcMesh.copy_mesh": {"tf": 1}, "vertax.BoundedMesh.copy_mesh": {"tf": 1}}, "df": 2}}}}, "n": {"docs": {"vertax.PbcBilevelOptimizer.compute_outer_loss": {"tf": 1.7320508075688772}, "vertax.PbcBilevelOptimizer.compute_inner_loss": {"tf": 1.7320508075688772}, "vertax.BoundedBilevelOptimizer.compute_outer_loss": {"tf": 1.7320508075688772}, "vertax.BoundedBilevelOptimizer.compute_inner_loss": {"tf": 1.7320508075688772}}, "df": 4, "l": {"docs": {}, "df": 0, "y": {"docs": {"vertax.PbcBilevelOptimizer.compute_outer_loss": {"tf": 1.7320508075688772}, "vertax.PbcBilevelOptimizer.compute_inner_loss": {"tf": 1.7320508075688772}, "vertax.BoundedBilevelOptimizer.compute_outer_loss": {"tf": 1.7320508075688772}, "vertax.BoundedBilevelOptimizer.compute_inner_loss": {"tf": 1.7320508075688772}}, "df": 4}}}}, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {"vertax.energy_line_tensions_bounded": {"tf": 1}}, "df": 1, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"vertax.PbcMesh.save_mesh_txt": {"tf": 1.4142135623730951}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1.4142135623730951}, "vertax.PbcBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.PbcBilevelOptimizer.compute_inner_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_inner_loss": {"tf": 1}, "vertax.plot_mesh": {"tf": 1.4142135623730951}, "vertax.get_plot_mesh": {"tf": 1.4142135623730951}}, "df": 10}}}}, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"vertax.PbcMesh.save_mesh_txt": {"tf": 1}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1}, "vertax.cost_v2v": {"tf": 1.4142135623730951}, "vertax.cost_mesh2image": {"tf": 1.4142135623730951}, "vertax.cost_areas": {"tf": 1.4142135623730951}, "vertax.cost_IAS": {"tf": 1.4142135623730951}, "vertax.cost_d_IAS": {"tf": 1.4142135623730951}, "vertax.cost_tem_halfedge": {"tf": 1.4142135623730951}, "vertax.cost_v2v_ias": {"tf": 1.4142135623730951}, "vertax.cost_v2v_tem": {"tf": 1.4142135623730951}, "vertax.cost_ratio": {"tf": 1.4142135623730951}, "vertax.cost_checkerboard": {"tf": 1.4142135623730951}, "vertax.energy_shape_factor_hetero": {"tf": 1}, "vertax.energy_shape_factor_homo": {"tf": 1}, "vertax.energy_line_tensions": {"tf": 1}, "vertax.energy_line_tensions_bounded": {"tf": 1}}, "df": 18}}}}}, "p": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"vertax.PbcMesh.save_mesh_txt": {"tf": 1}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1}}, "df": 4}}}}}}}}}}}, "a": {"docs": {}, "df": 0, "x": {"docs": {"vertax.PbcBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.PbcBilevelOptimizer.compute_inner_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_inner_loss": {"tf": 1}, "vertax.plot_mesh": {"tf": 2}, "vertax.get_plot_mesh": {"tf": 2}}, "df": 6}}, "e": {"docs": {}, "df": 0, "x": {"docs": {"vertax.plot_mesh": {"tf": 1.7320508075688772}, "vertax.get_plot_mesh": {"tf": 1.7320508075688772}}, "df": 2, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "t": {"docs": {"vertax.plot_mesh": {"tf": 1.4142135623730951}, "vertax.get_plot_mesh": {"tf": 1.4142135623730951}}, "df": 2}}}}}}, "s": {"docs": {"vertax.cost_v2v": {"tf": 1}, "vertax.cost_mesh2image": {"tf": 1}, "vertax.cost_areas": {"tf": 1}, "vertax.cost_IAS": {"tf": 1}, "vertax.cost_d_IAS": {"tf": 1}, "vertax.cost_tem_halfedge": {"tf": 1}, "vertax.cost_v2v_ias": {"tf": 1}, "vertax.cost_v2v_tem": {"tf": 1}, "vertax.cost_ratio": {"tf": 1}, "vertax.cost_checkerboard": {"tf": 1}, "vertax.energy_line_tensions_bounded": {"tf": 1}}, "df": 11}}}}}, "e": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {"vertax.PbcMesh.get_length": {"tf": 1}, "vertax.PbcMesh.get_length_with_offset": {"tf": 1}, "vertax.BoundedMesh.get_length": {"tf": 1}, "vertax.plot_mesh": {"tf": 1.7320508075688772}, "vertax.get_plot_mesh": {"tf": 1.7320508075688772}}, "df": 5, "s": {"docs": {"vertax.PbcMesh.save_mesh_txt": {"tf": 1.4142135623730951}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1.4142135623730951}, "vertax.PbcBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.PbcBilevelOptimizer.compute_inner_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_inner_loss": {"tf": 1}, "vertax.plot_mesh": {"tf": 1.4142135623730951}, "vertax.get_plot_mesh": {"tf": 1.4142135623730951}}, "df": 10}, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "t": {"docs": {"vertax.plot_mesh": {"tf": 1.4142135623730951}, "vertax.get_plot_mesh": {"tf": 1.4142135623730951}}, "df": 2}}}}}}}}, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {"vertax.PbcBilevelOptimizer.compute_outer_loss": {"tf": 1.7320508075688772}, "vertax.PbcBilevelOptimizer.compute_inner_loss": {"tf": 1.7320508075688772}, "vertax.BoundedBilevelOptimizer.compute_outer_loss": {"tf": 1.7320508075688772}, "vertax.BoundedBilevelOptimizer.compute_inner_loss": {"tf": 1.7320508075688772}}, "df": 4}}}, "t": {"docs": {"vertax.plot_mesh": {"tf": 1.7320508075688772}, "vertax.get_plot_mesh": {"tf": 1.7320508075688772}}, "df": 2}}, "g": {"docs": {}, "df": 0, "t": {"docs": {"vertax.plot_mesh": {"tf": 1.7320508075688772}, "vertax.get_plot_mesh": {"tf": 1.7320508075688772}}, "df": 2}}, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "k": {"docs": {"vertax.plot_mesh": {"tf": 1}, "vertax.get_plot_mesh": {"tf": 1}}, "df": 2}}}}, "o": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {"vertax.plot_mesh": {"tf": 1.4142135623730951}}, "df": 1}}}}}}, "bases": {"root": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "x": {"docs": {"vertax.PbcMesh": {"tf": 1}, "vertax.BoundedMesh": {"tf": 1}, "vertax.PbcBilevelOptimizer": {"tf": 1}, "vertax.BoundedBilevelOptimizer": {"tf": 1}}, "df": 4}}}}}}, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "h": {"docs": {"vertax.PbcMesh": {"tf": 1.4142135623730951}, "vertax.BoundedMesh": {"tf": 1.4142135623730951}}, "df": 2, "e": {"docs": {}, "df": 0, "s": {"docs": {"vertax.PbcMesh": {"tf": 1}, "vertax.BoundedMesh": {"tf": 1}}, "df": 2}}}}}}, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "m": {"docs": {"vertax.BilevelOptimizationMethod": {"tf": 1.4142135623730951}, "vertax.FacePlot": {"tf": 1.4142135623730951}, "vertax.EdgePlot": {"tf": 1.4142135623730951}, "vertax.VertexPlot": {"tf": 1.4142135623730951}}, "df": 4}}}}, "b": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "t": {"docs": {"vertax.PbcBilevelOptimizer": {"tf": 1.4142135623730951}, "vertax.BoundedBilevelOptimizer": {"tf": 1.4142135623730951}}, "df": 2, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "z": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"vertax.PbcBilevelOptimizer": {"tf": 1}, "vertax.BoundedBilevelOptimizer": {"tf": 1}}, "df": 2}}}}}}}}}}}}}}}}}}, "doc": {"root": {"0": {"1": {"docs": {"vertax.cost_v2v_tem": {"tf": 1}}, "df": 1}, "docs": {"vertax.Mesh.width": {"tf": 1}, "vertax.Mesh.height": {"tf": 1}, "vertax.PbcMesh": {"tf": 2}, "vertax.BoundedMesh": {"tf": 2.23606797749979}, "vertax.BoundedMesh.angles": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1.4142135623730951}, "vertax.BilevelOptimizationMethod.IMPLICIT_DIFFERENTIATION": {"tf": 1}, "vertax.BilevelOptimizationMethod.ADJOINT_STATE": {"tf": 1}, "vertax.cost_v2v_ias": {"tf": 1.4142135623730951}, "vertax.cost_v2v_tem": {"tf": 1.4142135623730951}}, "df": 10}, "1": {"docs": {"vertax.PbcMesh": {"tf": 2}, "vertax.BoundedMesh": {"tf": 1.4142135623730951}, "vertax.cost_IAS": {"tf": 1}, "vertax.cost_d_IAS": {"tf": 1}}, "df": 4, "d": {"docs": {"vertax.PbcMesh": {"tf": 1}, "vertax.BoundedMesh": {"tf": 1.4142135623730951}}, "df": 2}, "}": {"docs": {}, "df": 0, "^": {"docs": {}, "df": 0, "{": {"docs": {}, "df": 0, "n": {"docs": {"vertax.cost_IAS": {"tf": 1}, "vertax.cost_d_IAS": {"tf": 1}}, "df": 2}}}}}, "2": {"0": {"docs": {"vertax.PbcMesh.MAX_EDGES_IN_ANY_FACE": {"tf": 1}}, "df": 1}, "docs": {"vertax.PbcMesh": {"tf": 1}, "vertax.PbcMesh.from_seeds": {"tf": 1}, "vertax.BoundedMesh": {"tf": 2.449489742783178}, "vertax.BoundedMesh.angles": {"tf": 1}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1.4142135623730951}, "vertax.cost_IAS": {"tf": 1.4142135623730951}, "vertax.cost_d_IAS": {"tf": 1}, "vertax.cost_checkerboard": {"tf": 1}}, "df": 9, "d": {"docs": {"vertax.PbcMesh": {"tf": 1.4142135623730951}, "vertax.BoundedMesh": {"tf": 1.4142135623730951}}, "df": 2}}, "3": {"docs": {"vertax": {"tf": 1}}, "df": 1}, "4": {"docs": {"vertax.cost_v2v_ias": {"tf": 1}}, "df": 1}, "6": {"docs": {"vertax.cost_v2v_ias": {"tf": 1}}, "df": 1}, "8": {"docs": {"vertax.PbcMesh": {"tf": 1}, "vertax.BoundedMesh": {"tf": 1}}, "df": 2}, "9": {"9": {"docs": {"vertax.cost_v2v_tem": {"tf": 1}}, "df": 1}, "docs": {}, "df": 0}, "docs": {"vertax": {"tf": 10.14889156509222}, "vertax.Mesh": {"tf": 3.1622776601683795}, "vertax.Mesh.__init__": {"tf": 3.605551275463989}, "vertax.Mesh.faces": {"tf": 1.7320508075688772}, "vertax.Mesh.edges": {"tf": 1.7320508075688772}, "vertax.Mesh.vertices": {"tf": 1.7320508075688772}, "vertax.Mesh.width": {"tf": 1.7320508075688772}, "vertax.Mesh.height": {"tf": 1.7320508075688772}, "vertax.Mesh.faces_params": {"tf": 1.7320508075688772}, "vertax.Mesh.edges_params": {"tf": 1.7320508075688772}, "vertax.Mesh.vertices_params": {"tf": 1.7320508075688772}, "vertax.Mesh.nb_faces": {"tf": 1.7320508075688772}, "vertax.Mesh.nb_edges": {"tf": 1.7320508075688772}, "vertax.Mesh.nb_half_edges": {"tf": 1.7320508075688772}, "vertax.Mesh.nb_vertices": {"tf": 1.7320508075688772}, "vertax.Mesh.save_mesh": {"tf": 4.242640687119285}, "vertax.Mesh.load_mesh": {"tf": 4.898979485566356}, "vertax.PbcMesh": {"tf": 6.708203932499369}, "vertax.PbcMesh.__init__": {"tf": 5.5677643628300215}, "vertax.PbcMesh.MAX_EDGES_IN_ANY_FACE": {"tf": 2}, "vertax.PbcMesh.from_random_seeds": {"tf": 6.48074069840786}, "vertax.PbcMesh.from_seeds": {"tf": 5.477225575051661}, "vertax.PbcMesh.from_image": {"tf": 5.477225575051661}, "vertax.PbcMesh.from_mask": {"tf": 5.196152422706632}, "vertax.PbcMesh.create_empty": {"tf": 1.7320508075688772}, "vertax.PbcMesh.copy_mesh": {"tf": 1.7320508075688772}, "vertax.PbcMesh.save_mesh": {"tf": 4.242640687119285}, "vertax.PbcMesh.load_mesh": {"tf": 5.196152422706632}, "vertax.PbcMesh.save_mesh_txt": {"tf": 7.745966692414834}, "vertax.PbcMesh.load_mesh_txt": {"tf": 8.306623862918075}, "vertax.PbcMesh.get_length": {"tf": 1.7320508075688772}, "vertax.PbcMesh.get_length_with_offset": {"tf": 1.7320508075688772}, "vertax.PbcMesh.get_perimeter": {"tf": 1.7320508075688772}, "vertax.PbcMesh.get_area": {"tf": 1.7320508075688772}, "vertax.PbcMesh.update_boundary_conditions": {"tf": 2.8284271247461903}, "vertax.BoundedMesh": {"tf": 7.416198487095663}, "vertax.BoundedMesh.__init__": {"tf": 1.7320508075688772}, "vertax.BoundedMesh.angles": {"tf": 2}, "vertax.BoundedMesh.nb_angles": {"tf": 1.7320508075688772}, "vertax.BoundedMesh.from_random_seeds": {"tf": 6.928203230275509}, "vertax.BoundedMesh.from_seeds": {"tf": 6.48074069840786}, "vertax.BoundedMesh.create_empty": {"tf": 1.7320508075688772}, "vertax.BoundedMesh.copy_mesh": {"tf": 1.7320508075688772}, "vertax.BoundedMesh.save_mesh": {"tf": 4.242640687119285}, "vertax.BoundedMesh.load_mesh": {"tf": 5.196152422706632}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 8.12403840463596}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 8.660254037844387}, "vertax.BoundedMesh.get_length": {"tf": 1.7320508075688772}, "vertax.BoundedMesh.get_perimeter": {"tf": 1.7320508075688772}, "vertax.BoundedMesh.get_area": {"tf": 1.7320508075688772}, "vertax.BilevelOptimizationMethod": {"tf": 1.7320508075688772}, "vertax.BilevelOptimizationMethod.AUTOMATIC_DIFFERENTIATION": {"tf": 2.23606797749979}, "vertax.BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION": {"tf": 2}, "vertax.BilevelOptimizationMethod.IMPLICIT_DIFFERENTIATION": {"tf": 1.7320508075688772}, "vertax.BilevelOptimizationMethod.ADJOINT_STATE": {"tf": 1.7320508075688772}, "vertax.PbcBilevelOptimizer": {"tf": 2.23606797749979}, "vertax.PbcBilevelOptimizer.__init__": {"tf": 2.449489742783178}, "vertax.PbcBilevelOptimizer.compute_outer_loss": {"tf": 1.7320508075688772}, "vertax.PbcBilevelOptimizer.compute_inner_loss": {"tf": 1.7320508075688772}, "vertax.BoundedBilevelOptimizer": {"tf": 2.23606797749979}, "vertax.BoundedBilevelOptimizer.__init__": {"tf": 2.449489742783178}, "vertax.BoundedBilevelOptimizer.angles_target": {"tf": 1.7320508075688772}, "vertax.BoundedBilevelOptimizer.compute_outer_loss": {"tf": 2.449489742783178}, "vertax.BoundedBilevelOptimizer.compute_inner_loss": {"tf": 2.449489742783178}, "vertax.plot_mesh": {"tf": 1.7320508075688772}, "vertax.get_plot_mesh": {"tf": 1.7320508075688772}, "vertax.FacePlot": {"tf": 1.7320508075688772}, "vertax.FacePlot.MULTICOLOR": {"tf": 1.7320508075688772}, "vertax.FacePlot.FACE_PARAMETER": {"tf": 1.7320508075688772}, "vertax.FacePlot.AREA": {"tf": 1.7320508075688772}, "vertax.FacePlot.PERIMETER": {"tf": 1.7320508075688772}, "vertax.FacePlot.WHITE": {"tf": 1.7320508075688772}, "vertax.FacePlot.FATES": {"tf": 1.7320508075688772}, "vertax.EdgePlot": {"tf": 1.7320508075688772}, "vertax.EdgePlot.BLACK": {"tf": 1.7320508075688772}, "vertax.EdgePlot.EDGE_PARAMETER": {"tf": 1.7320508075688772}, "vertax.EdgePlot.LENGTH": {"tf": 1.7320508075688772}, "vertax.EdgePlot.INVISIBLE": {"tf": 1.7320508075688772}, "vertax.VertexPlot": {"tf": 1.7320508075688772}, "vertax.VertexPlot.BLACK": {"tf": 1.7320508075688772}, "vertax.VertexPlot.VERTEX_PARAMETER": {"tf": 1.7320508075688772}, "vertax.VertexPlot.INVISIBLE": {"tf": 1.7320508075688772}, "vertax.cost_v2v": {"tf": 2.23606797749979}, "vertax.cost_mesh2image": {"tf": 2.23606797749979}, "vertax.cost_areas": {"tf": 2.449489742783178}, "vertax.cost_IAS": {"tf": 2.23606797749979}, "vertax.cost_d_IAS": {"tf": 2.449489742783178}, "vertax.cost_tem_halfedge": {"tf": 1.7320508075688772}, "vertax.cost_v2v_ias": {"tf": 1.7320508075688772}, "vertax.cost_v2v_tem": {"tf": 1.7320508075688772}, "vertax.cost_ratio": {"tf": 2.23606797749979}, "vertax.cost_checkerboard": {"tf": 2.8284271247461903}, "vertax.energy_shape_factor_hetero": {"tf": 1.7320508075688772}, "vertax.energy_shape_factor_homo": {"tf": 1.7320508075688772}, "vertax.energy_line_tensions": {"tf": 1.7320508075688772}, "vertax.energy_line_tensions_bounded": {"tf": 1.7320508075688772}}, "df": 96, "a": {"docs": {"vertax": {"tf": 3.605551275463989}, "vertax.Mesh.__init__": {"tf": 1}, "vertax.Mesh.width": {"tf": 1}, "vertax.Mesh.height": {"tf": 1}, "vertax.Mesh.save_mesh": {"tf": 1}, "vertax.Mesh.load_mesh": {"tf": 1.4142135623730951}, "vertax.PbcMesh": {"tf": 2.8284271247461903}, "vertax.PbcMesh.MAX_EDGES_IN_ANY_FACE": {"tf": 1}, "vertax.PbcMesh.from_random_seeds": {"tf": 1.4142135623730951}, "vertax.PbcMesh.from_seeds": {"tf": 1.7320508075688772}, "vertax.PbcMesh.from_image": {"tf": 2}, "vertax.PbcMesh.from_mask": {"tf": 1.7320508075688772}, "vertax.PbcMesh.copy_mesh": {"tf": 1}, "vertax.PbcMesh.save_mesh": {"tf": 1.4142135623730951}, "vertax.PbcMesh.load_mesh": {"tf": 1.4142135623730951}, "vertax.PbcMesh.save_mesh_txt": {"tf": 1}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1}, "vertax.PbcMesh.update_boundary_conditions": {"tf": 1}, "vertax.BoundedMesh": {"tf": 2.449489742783178}, "vertax.BoundedMesh.from_random_seeds": {"tf": 2}, "vertax.BoundedMesh.from_seeds": {"tf": 2.23606797749979}, "vertax.BoundedMesh.copy_mesh": {"tf": 1}, "vertax.BoundedMesh.save_mesh": {"tf": 1}, "vertax.BoundedMesh.load_mesh": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.get_perimeter": {"tf": 1}, "vertax.BoundedMesh.get_area": {"tf": 1}, "vertax.PbcBilevelOptimizer.__init__": {"tf": 1}, "vertax.BoundedBilevelOptimizer.__init__": {"tf": 1}, "vertax.FacePlot": {"tf": 1}, "vertax.FacePlot.MULTICOLOR": {"tf": 1}, "vertax.VertexPlot": {"tf": 1}, "vertax.cost_mesh2image": {"tf": 1}, "vertax.cost_d_IAS": {"tf": 1.4142135623730951}, "vertax.cost_ratio": {"tf": 1}, "vertax.cost_checkerboard": {"tf": 1.4142135623730951}}, "df": 37, "n": {"docs": {"vertax": {"tf": 1}, "vertax.Mesh": {"tf": 1}, "vertax.PbcMesh.from_image": {"tf": 1}, "vertax.PbcMesh.create_empty": {"tf": 1}, "vertax.PbcMesh.get_length_with_offset": {"tf": 1}, "vertax.PbcMesh.update_boundary_conditions": {"tf": 1}, "vertax.BoundedMesh": {"tf": 2}, "vertax.BoundedMesh.create_empty": {"tf": 1}, "vertax.BoundedMesh.get_length": {"tf": 1}, "vertax.EdgePlot": {"tf": 1}}, "df": 10, "d": {"docs": {"vertax": {"tf": 3.4641016151377544}, "vertax.Mesh": {"tf": 1.4142135623730951}, "vertax.Mesh.__init__": {"tf": 1}, "vertax.PbcMesh.from_seeds": {"tf": 1}, "vertax.PbcMesh.from_image": {"tf": 1.4142135623730951}, "vertax.PbcMesh.save_mesh_txt": {"tf": 1}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1}, "vertax.BoundedMesh": {"tf": 1}, "vertax.BoundedMesh.angles": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1}, "vertax.BilevelOptimizationMethod.AUTOMATIC_DIFFERENTIATION": {"tf": 1}, "vertax.BilevelOptimizationMethod.IMPLICIT_DIFFERENTIATION": {"tf": 1}, "vertax.PbcBilevelOptimizer.__init__": {"tf": 1}, "vertax.BoundedBilevelOptimizer.__init__": {"tf": 1}, "vertax.plot_mesh": {"tf": 1}, "vertax.get_plot_mesh": {"tf": 1.4142135623730951}, "vertax.cost_v2v_ias": {"tf": 1.4142135623730951}, "vertax.cost_v2v_tem": {"tf": 1.4142135623730951}, "vertax.energy_line_tensions": {"tf": 1}, "vertax.energy_line_tensions_bounded": {"tf": 1}}, "df": 22, "/": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {"vertax.plot_mesh": {"tf": 1}}, "df": 1}}}}, "o": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"vertax.PbcMesh.copy_mesh": {"tf": 1}, "vertax.BoundedMesh.copy_mesh": {"tf": 1}}, "df": 2}}}}}, "y": {"docs": {"vertax.PbcMesh.save_mesh_txt": {"tf": 1}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1}, "vertax.FacePlot.FATES": {"tf": 1}}, "df": 4}, "g": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"vertax.BoundedMesh.angles": {"tf": 1}}, "df": 1, "s": {"docs": {"vertax.BoundedMesh": {"tf": 1.7320508075688772}, "vertax.BoundedMesh.nb_angles": {"tf": 1}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1.7320508075688772}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1.7320508075688772}}, "df": 4}}}, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"vertax.BoundedMesh.save_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1}}, "df": 2}}}}}}}, "m": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}, "p": {"docs": {"vertax": {"tf": 2.449489742783178}}, "df": 1}}, "d": {"docs": {}, "df": 0, "d": {"docs": {"vertax.BoundedMesh.from_random_seeds": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}}, "df": 2, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}}, "s": {"docs": {"vertax": {"tf": 2.23606797749979}, "vertax.PbcMesh.__init__": {"tf": 1}, "vertax.PbcMesh.from_image": {"tf": 1}, "vertax.PbcMesh.from_mask": {"tf": 1}, "vertax.PbcMesh.update_boundary_conditions": {"tf": 1}}, "df": 5, "s": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"vertax.PbcMesh.from_seeds": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}}, "df": 2}}}}}}, "r": {"docs": {}, "df": 0, "e": {"docs": {"vertax": {"tf": 2.8284271247461903}, "vertax.PbcMesh.from_seeds": {"tf": 1}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1}, "vertax.FacePlot.WHITE": {"tf": 1}, "vertax.FacePlot.FATES": {"tf": 1}, "vertax.EdgePlot.BLACK": {"tf": 1}, "vertax.VertexPlot.BLACK": {"tf": 1}}, "df": 9, "a": {"docs": {"vertax.PbcMesh.get_area": {"tf": 1}, "vertax.BoundedMesh.get_perimeter": {"tf": 1}, "vertax.BoundedMesh.get_area": {"tf": 1}, "vertax.FacePlot.AREA": {"tf": 1}}, "df": 4, "s": {"docs": {"vertax": {"tf": 1}, "vertax.cost_areas": {"tf": 1.7320508075688772}, "vertax.energy_line_tensions": {"tf": 1}, "vertax.energy_line_tensions_bounded": {"tf": 1}}, "df": 4}}}, "g": {"docs": {}, "df": 0, "\\": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {"vertax": {"tf": 1.4142135623730951}}, "df": 1}}}}, "u": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "s": {"docs": {"vertax.Mesh.save_mesh": {"tf": 1}, "vertax.Mesh.load_mesh": {"tf": 1}, "vertax.PbcMesh.from_random_seeds": {"tf": 1}, "vertax.PbcMesh.from_seeds": {"tf": 1}, "vertax.PbcMesh.from_image": {"tf": 1}, "vertax.PbcMesh.from_mask": {"tf": 1}, "vertax.PbcMesh.save_mesh": {"tf": 1}, "vertax.PbcMesh.load_mesh": {"tf": 1}, "vertax.PbcMesh.save_mesh_txt": {"tf": 1}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}, "vertax.BoundedMesh.save_mesh": {"tf": 1}, "vertax.BoundedMesh.load_mesh": {"tf": 1}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1}, "vertax.PbcBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.PbcBilevelOptimizer.compute_inner_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_inner_loss": {"tf": 1}}, "df": 20}}}}}}}, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "y": {"docs": {"vertax.PbcMesh": {"tf": 1.7320508075688772}, "vertax.PbcMesh.from_seeds": {"tf": 1}, "vertax.PbcMesh.get_length_with_offset": {"tf": 1}, "vertax.BoundedMesh": {"tf": 2}, "vertax.BoundedMesh.from_seeds": {"tf": 1}}, "df": 5, "[": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"3": {"2": {"docs": {"vertax.PbcMesh.from_seeds": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}}, "df": 2}, "docs": {}, "df": 0}, "docs": {}, "df": 0}}}}}}}}}, "c": {"docs": {"vertax.BoundedMesh": {"tf": 1}}, "df": 1, "s": {"docs": {"vertax.BoundedMesh": {"tf": 1}}, "df": 1}, "e": {"docs": {}, "df": 0, "d": {"docs": {"vertax.BoundedMesh.angles": {"tf": 1}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1}}, "df": 2}}}}, "l": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "o": {"docs": {"vertax": {"tf": 1}}, "df": 1}}, "l": {"docs": {"vertax.Mesh.save_mesh": {"tf": 1}, "vertax.PbcMesh.copy_mesh": {"tf": 1}, "vertax.PbcMesh.save_mesh": {"tf": 1}, "vertax.PbcMesh.load_mesh": {"tf": 1}, "vertax.BoundedMesh.copy_mesh": {"tf": 1}, "vertax.BoundedMesh.save_mesh": {"tf": 1}, "vertax.BoundedMesh.load_mesh": {"tf": 1}, "vertax.FacePlot.WHITE": {"tf": 1}, "vertax.EdgePlot.BLACK": {"tf": 1}}, "df": 9, "o": {"docs": {}, "df": 0, "w": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}, "w": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "s": {"docs": {"vertax.PbcMesh.from_image": {"tf": 1}}, "df": 1}}}}, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"vertax.PbcMesh.get_length_with_offset": {"tf": 1}, "vertax.cost_ratio": {"tf": 1.4142135623730951}}, "df": 2}}}}, "b": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"vertax": {"tf": 1}, "vertax.Mesh": {"tf": 1}}, "df": 2}}}}}}}, "t": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"vertax.Mesh": {"tf": 1}, "vertax.Mesh.__init__": {"tf": 1}}, "df": 2}}}}}}}, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"vertax.Mesh.faces_params": {"tf": 1}, "vertax.Mesh.edges_params": {"tf": 1}, "vertax.Mesh.vertices_params": {"tf": 1}}, "df": 3}}}}}}}, "c": {"docs": {}, "df": 0, "t": {"docs": {"vertax.PbcMesh.from_image": {"tf": 1}, "vertax.PbcMesh.from_mask": {"tf": 1}}, "df": 2}}, "g": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {"vertax.PbcMesh.update_boundary_conditions": {"tf": 1}}, "df": 1}}}}, "f": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"vertax.PbcMesh.update_boundary_conditions": {"tf": 1}}, "df": 1}}}}, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {"vertax.PbcMesh.update_boundary_conditions": {"tf": 1}}, "df": 1}}}}}}}}}}}}, "x": {"docs": {"vertax.get_plot_mesh": {"tf": 1}}, "df": 1, "i": {"docs": {}, "df": 0, "s": {"docs": {"vertax.cost_ratio": {"tf": 1.4142135623730951}}, "df": 1}}}, "w": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"vertax.cost_IAS": {"tf": 1}, "vertax.cost_d_IAS": {"tf": 1}}, "df": 2}}}}, "v": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {"vertax.cost_checkerboard": {"tf": 1}}, "df": 1}}}}}, "d": {"docs": {"vertax.cost_d_IAS": {"tf": 1}}, "df": 1, "i": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"vertax.BoundedMesh.from_random_seeds": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}, "vertax.cost_checkerboard": {"tf": 1}}, "df": 3, "i": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"vertax": {"tf": 1}, "vertax.cost_IAS": {"tf": 1}}, "df": 2}}}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"vertax.BilevelOptimizationMethod.IMPLICIT_DIFFERENTIATION": {"tf": 1}, "vertax.BilevelOptimizationMethod.ADJOINT_STATE": {"tf": 1}}, "df": 2}}}}}}}}}}}}, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}}}}}, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {"vertax.Mesh": {"tf": 1}, "vertax.PbcMesh.__init__": {"tf": 1}, "vertax.PbcMesh.from_image": {"tf": 1}}, "df": 3}}, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"vertax.Mesh.width": {"tf": 1}, "vertax.Mesh.height": {"tf": 1}}, "df": 2}}}, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "y": {"docs": {"vertax.PbcMesh.save_mesh_txt": {"tf": 1.4142135623730951}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1.4142135623730951}}, "df": 4}}}}}}}, "a": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {"vertax.PbcMesh.from_random_seeds": {"tf": 1}, "vertax.PbcMesh.from_seeds": {"tf": 1}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1}}, "df": 3}}}}}, "s": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"vertax.cost_d_IAS": {"tf": 1}}, "df": 1}}}}}}}, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "n": {"docs": {"vertax": {"tf": 1.7320508075688772}}, "df": 1, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}, "e": {"docs": {}, "df": 0, "d": {"docs": {"vertax": {"tf": 1.4142135623730951}}, "df": 1}}}}}}, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}}}}}}}, "f": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {"vertax": {"tf": 1.4142135623730951}}, "df": 1, "d": {"docs": {"vertax": {"tf": 1.4142135623730951}}, "df": 1}, "s": {"docs": {"vertax": {"tf": 1}, "vertax.Mesh": {"tf": 1}}, "df": 2}}}}, "a": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "t": {"docs": {"vertax.BoundedMesh.from_random_seeds": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}, "vertax.PbcBilevelOptimizer.__init__": {"tf": 1}, "vertax.BoundedBilevelOptimizer.__init__": {"tf": 1}}, "df": 4, "s": {"docs": {"vertax.PbcMesh.save_mesh_txt": {"tf": 2.6457513110645907}, "vertax.PbcMesh.load_mesh_txt": {"tf": 2.6457513110645907}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 2.8284271247461903}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 2.8284271247461903}}, "df": 4}}}}}}, "n": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"vertax": {"tf": 1.4142135623730951}}, "df": 1}}}}}, "p": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"vertax.PbcMesh": {"tf": 1.4142135623730951}, "vertax.FacePlot.FATES": {"tf": 1}}, "df": 2}}}, "s": {"docs": {"vertax.BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION": {"tf": 1}, "vertax.FacePlot.FACE_PARAMETER": {"tf": 1}, "vertax.FacePlot.AREA": {"tf": 1}, "vertax.FacePlot.PERIMETER": {"tf": 1}, "vertax.EdgePlot.EDGE_PARAMETER": {"tf": 1}, "vertax.EdgePlot.LENGTH": {"tf": 1}, "vertax.VertexPlot.VERTEX_PARAMETER": {"tf": 1}, "vertax.energy_shape_factor_hetero": {"tf": 1}}, "df": 8}}}}}, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"vertax.PbcMesh.__init__": {"tf": 1}}, "df": 1}}}}}}}, "c": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {"vertax.BoundedMesh.from_random_seeds": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}, "vertax.plot_mesh": {"tf": 1}}, "df": 3}}}}}, "y": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}, "s": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {"vertax": {"tf": 1.4142135623730951}, "vertax.Mesh.save_mesh": {"tf": 1}, "vertax.PbcMesh.save_mesh": {"tf": 1}, "vertax.PbcMesh.load_mesh": {"tf": 1}, "vertax.BoundedMesh.save_mesh": {"tf": 1}, "vertax.BoundedMesh.load_mesh": {"tf": 1}}, "df": 6}}}, "o": {"docs": {"vertax": {"tf": 1}, "vertax.Mesh.__init__": {"tf": 1.4142135623730951}, "vertax.PbcMesh.__init__": {"tf": 1}, "vertax.PbcMesh.from_image": {"tf": 1}, "vertax.BoundedMesh.__init__": {"tf": 1}, "vertax.EdgePlot.INVISIBLE": {"tf": 1}, "vertax.VertexPlot.INVISIBLE": {"tf": 1}}, "df": 7, "c": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}}}}}}}, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {"vertax.PbcMesh.from_random_seeds": {"tf": 1.4142135623730951}}, "df": 1}}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"vertax.PbcMesh.create_empty": {"tf": 1}, "vertax.BoundedMesh.create_empty": {"tf": 1}}, "df": 2}}}, "n": {"docs": {"vertax.BoundedMesh.from_seeds": {"tf": 1}}, "df": 1}, "e": {"docs": {}, "df": 0, "s": {"docs": {"vertax.PbcBilevelOptimizer.__init__": {"tf": 1}, "vertax.BoundedBilevelOptimizer.__init__": {"tf": 1}}, "df": 2}}}, "c": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {"vertax.Mesh.__init__": {"tf": 1}}, "df": 1}}}, "u": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"vertax.cost_tem_halfedge": {"tf": 1}}, "df": 1}}}}, "j": {"docs": {"vertax.cost_IAS": {"tf": 1.4142135623730951}, "vertax.cost_d_IAS": {"tf": 1.4142135623730951}}, "df": 2, "a": {"docs": {}, "df": 0, "x": {"docs": {"vertax": {"tf": 1.4142135623730951}, "vertax.PbcMesh.from_seeds": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}, "vertax.BilevelOptimizationMethod.AUTOMATIC_DIFFERENTIATION": {"tf": 1}}, "df": 4}, "c": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "d": {"docs": {"vertax.BilevelOptimizationMethod.AUTOMATIC_DIFFERENTIATION": {"tf": 1}}, "df": 1}}}}}, "u": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}}}, "s": {"docs": {}, "df": 0, "t": {"docs": {"vertax.FacePlot.WHITE": {"tf": 1}}, "df": 1}}}, "v": {"docs": {}, "df": 0, "p": {"docs": {"vertax.BilevelOptimizationMethod.AUTOMATIC_DIFFERENTIATION": {"tf": 1}, "vertax.BilevelOptimizationMethod.IMPLICIT_DIFFERENTIATION": {"tf": 1}}, "df": 2}}}, "b": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"vertax": {"tf": 1}, "vertax.Mesh": {"tf": 1}, "vertax.PbcMesh.MAX_EDGES_IN_ANY_FACE": {"tf": 1}}, "df": 3, "d": {"docs": {"vertax": {"tf": 1.4142135623730951}, "vertax.PbcMesh.from_random_seeds": {"tf": 1}, "vertax.PbcMesh.from_seeds": {"tf": 1}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1}}, "df": 4}}, "i": {"docs": {}, "df": 0, "c": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}, "c": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "p": {"docs": {"vertax.BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION": {"tf": 1}}, "df": 1}}}}}}}, "i": {"docs": {"vertax.BilevelOptimizationMethod": {"tf": 1}, "vertax.PbcBilevelOptimizer": {"tf": 1}, "vertax.PbcBilevelOptimizer.__init__": {"tf": 1}, "vertax.BoundedBilevelOptimizer": {"tf": 1}, "vertax.BoundedBilevelOptimizer.__init__": {"tf": 1}}, "df": 5, "o": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "y": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}, "p": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "s": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}}}}, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {"vertax": {"tf": 1.4142135623730951}}, "df": 1, "o": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "z": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}}}}}}}}}}}, "u": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "t": {"docs": {"vertax": {"tf": 1.4142135623730951}}, "df": 1}}}, "t": {"docs": {"vertax": {"tf": 1.7320508075688772}, "vertax.Mesh.__init__": {"tf": 1.4142135623730951}, "vertax.BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION": {"tf": 1}}, "df": 3}, "l": {"docs": {}, "df": 0, "k": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}, "y": {"docs": {"vertax": {"tf": 1}, "vertax.PbcMesh.from_image": {"tf": 1}, "vertax.PbcMesh.from_mask": {"tf": 1}, "vertax.PbcMesh.save_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_inner_loss": {"tf": 1}, "vertax.energy_line_tensions": {"tf": 1}, "vertax.energy_line_tensions_bounded": {"tf": 1}}, "df": 9}, "e": {"docs": {"vertax.Mesh": {"tf": 1}, "vertax.Mesh.faces_params": {"tf": 1}, "vertax.Mesh.edges_params": {"tf": 1}, "vertax.Mesh.vertices_params": {"tf": 1}, "vertax.PbcMesh.MAX_EDGES_IN_ANY_FACE": {"tf": 1}, "vertax.PbcMesh.from_random_seeds": {"tf": 1.4142135623730951}, "vertax.PbcMesh.from_image": {"tf": 1.4142135623730951}, "vertax.PbcMesh.save_mesh_txt": {"tf": 1}, "vertax.PbcMesh.update_boundary_conditions": {"tf": 1}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_inner_loss": {"tf": 1}, "vertax.energy_shape_factor_homo": {"tf": 1}}, "df": 13, "t": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {"vertax": {"tf": 1}, "vertax.Mesh": {"tf": 1}, "vertax.Mesh.edges": {"tf": 1}, "vertax.BoundedMesh": {"tf": 1}, "vertax.BoundedMesh.angles": {"tf": 1}}, "df": 5}}}}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"vertax": {"tf": 1}, "vertax.PbcMesh.from_image": {"tf": 1}}, "df": 2}}}}, "g": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "{": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"vertax": {"tf": 1.4142135623730951}}, "df": 1}}}}}}}}}}}, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"vertax.PbcMesh": {"tf": 1}, "vertax.BoundedMesh": {"tf": 1}}, "df": 2}}}}}}}}, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "y": {"docs": {"vertax": {"tf": 1}, "vertax.PbcMesh": {"tf": 1.7320508075688772}, "vertax.PbcMesh.from_random_seeds": {"tf": 1}, "vertax.PbcMesh.from_seeds": {"tf": 1}, "vertax.PbcMesh.from_image": {"tf": 1}, "vertax.PbcMesh.from_mask": {"tf": 1}, "vertax.PbcMesh.update_boundary_conditions": {"tf": 1}, "vertax.BoundedMesh": {"tf": 1}, "vertax.PbcBilevelOptimizer": {"tf": 1}, "vertax.PbcBilevelOptimizer.__init__": {"tf": 1}}, "df": 10}, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}, "e": {"docs": {}, "df": 0, "d": {"docs": {"vertax.BoundedMesh": {"tf": 1}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1.4142135623730951}, "vertax.BoundedBilevelOptimizer": {"tf": 1}, "vertax.BoundedBilevelOptimizer.__init__": {"tf": 1}, "vertax.energy_line_tensions_bounded": {"tf": 1}}, "df": 6, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "h": {"docs": {"vertax": {"tf": 1.4142135623730951}, "vertax.Mesh": {"tf": 1}, "vertax.Mesh.__init__": {"tf": 1}, "vertax.BoundedMesh": {"tf": 1}, "vertax.BoundedMesh.save_mesh": {"tf": 1}, "vertax.BoundedMesh.load_mesh": {"tf": 1}, "vertax.BoundedBilevelOptimizer": {"tf": 1}, "vertax.cost_ratio": {"tf": 1}, "vertax.cost_checkerboard": {"tf": 1}}, "df": 9}}}}, "b": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "z": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}}}}}}}}}}}}}}}}}, "x": {"docs": {"vertax.PbcMesh.from_seeds": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.from_seeds": {"tf": 1.4142135623730951}}, "df": 3}}, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "k": {"docs": {"vertax.EdgePlot.BLACK": {"tf": 1}, "vertax.VertexPlot.BLACK": {"tf": 1}}, "df": 2}}}}, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"vertax.energy_shape_factor_homo": {"tf": 1}}, "df": 1}}}}}}}}}}}, "f": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "k": {"docs": {"vertax": {"tf": 1.7320508075688772}}, "df": 1}}}}}}}, "e": {"docs": {}, "df": 0, "e": {"docs": {"vertax": {"tf": 1}, "vertax.BoundedMesh": {"tf": 1}, "vertax.BoundedMesh.angles": {"tf": 1}, "vertax.BoundedMesh.nb_angles": {"tf": 1}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1}, "vertax.BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION": {"tf": 1}}, "df": 6}}, "o": {"docs": {}, "df": 0, "m": {"docs": {"vertax.Mesh.load_mesh": {"tf": 1.4142135623730951}, "vertax.PbcMesh.__init__": {"tf": 2}, "vertax.PbcMesh.from_random_seeds": {"tf": 1}, "vertax.PbcMesh.from_seeds": {"tf": 1}, "vertax.PbcMesh.from_image": {"tf": 1.4142135623730951}, "vertax.PbcMesh.from_mask": {"tf": 1}, "vertax.PbcMesh.copy_mesh": {"tf": 1}, "vertax.PbcMesh.load_mesh": {"tf": 1.4142135623730951}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}, "vertax.BoundedMesh.copy_mesh": {"tf": 1}, "vertax.BoundedMesh.load_mesh": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1}, "vertax.BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION": {"tf": 1}}, "df": 15}}}, "o": {"docs": {}, "df": 0, "r": {"docs": {"vertax": {"tf": 3.7416573867739413}, "vertax.PbcMesh": {"tf": 1}, "vertax.PbcMesh.from_random_seeds": {"tf": 1}, "vertax.PbcMesh.from_image": {"tf": 1}, "vertax.PbcMesh.from_mask": {"tf": 1}, "vertax.PbcMesh.save_mesh_txt": {"tf": 2.6457513110645907}, "vertax.PbcMesh.load_mesh_txt": {"tf": 2.6457513110645907}, "vertax.BoundedMesh": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.from_seeds": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 2.8284271247461903}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 2.8284271247461903}, "vertax.PbcBilevelOptimizer": {"tf": 1}, "vertax.PbcBilevelOptimizer.__init__": {"tf": 1}, "vertax.BoundedBilevelOptimizer": {"tf": 1}, "vertax.BoundedBilevelOptimizer.__init__": {"tf": 1}, "vertax.get_plot_mesh": {"tf": 1}, "vertax.energy_line_tensions": {"tf": 1}, "vertax.energy_line_tensions_bounded": {"tf": 1}}, "df": 19, "w": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "d": {"docs": {"vertax": {"tf": 1}, "vertax.BilevelOptimizationMethod.AUTOMATIC_DIFFERENTIATION": {"tf": 1}}, "df": 2}}}}, "m": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {"vertax.PbcMesh.save_mesh": {"tf": 1}, "vertax.PbcMesh.load_mesh": {"tf": 1}}, "df": 2}}}, "c": {"docs": {}, "df": 0, "e": {"docs": {"vertax.PbcMesh.update_boundary_conditions": {"tf": 1}, "vertax.cost_IAS": {"tf": 1}}, "df": 2}}}, "l": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {"vertax.PbcMesh": {"tf": 1.4142135623730951}, "vertax.PbcMesh.MAX_EDGES_IN_ANY_FACE": {"tf": 1}, "vertax.PbcMesh.save_mesh_txt": {"tf": 1}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1}, "vertax.BoundedMesh": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.get_perimeter": {"tf": 1}, "vertax.BoundedMesh.get_area": {"tf": 1}, "vertax.FacePlot": {"tf": 1}, "vertax.FacePlot.MULTICOLOR": {"tf": 1}, "vertax.FacePlot.FACE_PARAMETER": {"tf": 1}, "vertax.FacePlot.AREA": {"tf": 1}, "vertax.FacePlot.PERIMETER": {"tf": 1}, "vertax.energy_shape_factor_homo": {"tf": 1}}, "df": 14, "s": {"docs": {"vertax": {"tf": 1.4142135623730951}, "vertax.Mesh.faces_params": {"tf": 1}, "vertax.Mesh.nb_faces": {"tf": 1}, "vertax.PbcMesh": {"tf": 1.4142135623730951}, "vertax.PbcMesh.save_mesh_txt": {"tf": 2.23606797749979}, "vertax.PbcMesh.load_mesh_txt": {"tf": 2.23606797749979}, "vertax.PbcMesh.get_perimeter": {"tf": 1}, "vertax.PbcMesh.get_area": {"tf": 1}, "vertax.BoundedMesh": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 2.23606797749979}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 2.23606797749979}, "vertax.FacePlot.WHITE": {"tf": 1}, "vertax.FacePlot.FATES": {"tf": 1}}, "df": 13}, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"vertax.PbcMesh.save_mesh_txt": {"tf": 1}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1}}, "df": 4}}}}}, "p": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"vertax.PbcMesh.save_mesh_txt": {"tf": 1}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1}}, "df": 4}}}}}}}}}}}}, "t": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {"vertax.energy_shape_factor_hetero": {"tf": 1}, "vertax.energy_shape_factor_homo": {"tf": 1}}, "df": 2, "s": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}, "t": {"docs": {}, "df": 0, "e": {"docs": {"vertax.BoundedMesh.from_random_seeds": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}, "vertax.FacePlot.FATES": {"tf": 1}, "vertax.cost_checkerboard": {"tf": 1}}, "df": 4, "s": {"docs": {"vertax.BoundedMesh.from_random_seeds": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.from_seeds": {"tf": 1.4142135623730951}, "vertax.cost_checkerboard": {"tf": 1}}, "df": 3}, "d": {"docs": {"vertax.cost_checkerboard": {"tf": 1}}, "df": 1}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}, "a": {"docs": {}, "df": 0, "l": {"docs": {"vertax.BoundedMesh.from_seeds": {"tf": 1}}, "df": 1, "l": {"docs": {}, "df": 0, "y": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}, "r": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}, "l": {"docs": {}, "df": 0, "e": {"docs": {"vertax.Mesh.save_mesh": {"tf": 1.4142135623730951}, "vertax.Mesh.load_mesh": {"tf": 1.7320508075688772}, "vertax.PbcMesh.save_mesh": {"tf": 1.4142135623730951}, "vertax.PbcMesh.load_mesh": {"tf": 1.7320508075688772}, "vertax.BoundedMesh.save_mesh": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.load_mesh": {"tf": 1.7320508075688772}}, "df": 6, "s": {"docs": {"vertax.PbcMesh.save_mesh_txt": {"tf": 1.4142135623730951}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1.4142135623730951}}, "df": 4}, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"vertax.PbcMesh.save_mesh_txt": {"tf": 3.7416573867739413}, "vertax.PbcMesh.load_mesh_txt": {"tf": 3.7416573867739413}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 4}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 4}}, "df": 4}}}}}, "l": {"docs": {"vertax.PbcMesh.from_image": {"tf": 1}}, "df": 1}}, "g": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"vertax.get_plot_mesh": {"tf": 1}}, "df": 1}}}}}, "u": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"vertax": {"tf": 1}, "vertax.PbcMesh.from_image": {"tf": 1}, "vertax.BilevelOptimizationMethod.IMPLICIT_DIFFERENTIATION": {"tf": 1}, "vertax.BilevelOptimizationMethod.ADJOINT_STATE": {"tf": 1}, "vertax.PbcBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.PbcBilevelOptimizer.compute_inner_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_inner_loss": {"tf": 1}}, "df": 8, "s": {"docs": {"vertax": {"tf": 2.449489742783178}, "vertax.Mesh": {"tf": 1}, "vertax.BoundedBilevelOptimizer.__init__": {"tf": 1}}, "df": 3}}}}}}}}, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {"vertax.PbcMesh.from_random_seeds": {"tf": 1.4142135623730951}, "vertax.PbcMesh.from_seeds": {"tf": 1.7320508075688772}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.from_seeds": {"tf": 1.7320508075688772}}, "df": 4, "s": {"docs": {"vertax.PbcMesh": {"tf": 1}, "vertax.BoundedMesh": {"tf": 1.4142135623730951}}, "df": 2}}}}}}, "v": {"2": {"docs": {}, "df": 0, "v": {"docs": {"vertax.cost_v2v_ias": {"tf": 1}, "vertax.cost_v2v_tem": {"tf": 1}}, "df": 2}}, "docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "x": {"docs": {"vertax": {"tf": 2}, "vertax.PbcMesh": {"tf": 2}, "vertax.BoundedMesh": {"tf": 2}, "vertax.VertexPlot": {"tf": 1}, "vertax.VertexPlot.VERTEX_PARAMETER": {"tf": 1}, "vertax.cost_v2v": {"tf": 1.4142135623730951}}, "df": 6}}, "a": {"docs": {}, "df": 0, "x": {"docs": {"vertax": {"tf": 2.8284271247461903}}, "df": 1}}, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"vertax": {"tf": 1.4142135623730951}, "vertax.Mesh.vertices": {"tf": 1}, "vertax.Mesh.vertices_params": {"tf": 1}, "vertax.Mesh.nb_vertices": {"tf": 1}, "vertax.PbcMesh": {"tf": 1.7320508075688772}, "vertax.PbcMesh.save_mesh_txt": {"tf": 2.23606797749979}, "vertax.PbcMesh.load_mesh_txt": {"tf": 2.23606797749979}, "vertax.BoundedMesh": {"tf": 1.7320508075688772}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 2.23606797749979}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 2.23606797749979}, "vertax.VertexPlot.BLACK": {"tf": 1}, "vertax.VertexPlot.INVISIBLE": {"tf": 1}, "vertax.cost_v2v": {"tf": 1.4142135623730951}, "vertax.cost_mesh2image": {"tf": 1}}, "df": 14}}}}, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"vertax.PbcMesh.save_mesh_txt": {"tf": 1}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1}}, "df": 4}}}}}, "p": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"vertax.PbcMesh.save_mesh_txt": {"tf": 1}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1}}, "df": 4}}}}}}}}}}}}, "s": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "s": {"docs": {"vertax.cost_areas": {"tf": 1}}, "df": 1}}}}}, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "e": {"docs": {"vertax.PbcMesh.MAX_EDGES_IN_ANY_FACE": {"tf": 1}}, "df": 1}}}, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"vertax.BilevelOptimizationMethod.IMPLICIT_DIFFERENTIATION": {"tf": 1}, "vertax.BilevelOptimizationMethod.ADJOINT_STATE": {"tf": 1}}, "df": 2}}}}}}, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "i": {"docs": {"vertax.PbcMesh.from_random_seeds": {"tf": 1}, "vertax.PbcMesh.from_seeds": {"tf": 1}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1}}, "df": 3}}}}}}, "i": {"docs": {}, "df": 0, "a": {"docs": {"vertax.BoundedMesh.from_seeds": {"tf": 1}, "vertax.BilevelOptimizationMethod.AUTOMATIC_DIFFERENTIATION": {"tf": 1}, "vertax.BilevelOptimizationMethod.IMPLICIT_DIFFERENTIATION": {"tf": 1}, "vertax.BilevelOptimizationMethod.ADJOINT_STATE": {"tf": 1}}, "df": 4}}, "s": {"docs": {"vertax.BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION": {"tf": 1}}, "df": 1}, "j": {"docs": {}, "df": 0, "p": {"docs": {"vertax.BilevelOptimizationMethod.ADJOINT_STATE": {"tf": 1}}, "df": 1}}}, "m": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {"vertax.BilevelOptimizationMethod.AUTOMATIC_DIFFERENTIATION": {"tf": 1}}, "df": 1, "l": {"docs": {"vertax": {"tf": 1.4142135623730951}}, "df": 1, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"vertax": {"tf": 1.4142135623730951}}, "df": 1}}}}}}, "r": {"docs": {}, "df": 0, "e": {"docs": {"vertax": {"tf": 1.4142135623730951}, "vertax.PbcMesh.MAX_EDGES_IN_ANY_FACE": {"tf": 1}}, "df": 2}}, "s": {"docs": {}, "df": 0, "t": {"docs": {"vertax.PbcMesh.update_boundary_conditions": {"tf": 1}, "vertax.BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION": {"tf": 1}}, "df": 2}}}, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"vertax": {"tf": 2}}, "df": 1}}, "s": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}}}, "s": {"docs": {}, "df": 0, "h": {"docs": {"vertax": {"tf": 1.7320508075688772}, "vertax.Mesh": {"tf": 1}, "vertax.Mesh.vertices": {"tf": 1}, "vertax.Mesh.width": {"tf": 1}, "vertax.Mesh.height": {"tf": 1}, "vertax.Mesh.nb_faces": {"tf": 1}, "vertax.Mesh.nb_edges": {"tf": 1}, "vertax.Mesh.nb_half_edges": {"tf": 1}, "vertax.Mesh.nb_vertices": {"tf": 1}, "vertax.Mesh.save_mesh": {"tf": 1.4142135623730951}, "vertax.Mesh.load_mesh": {"tf": 1.7320508075688772}, "vertax.PbcMesh": {"tf": 1}, "vertax.PbcMesh.from_random_seeds": {"tf": 1.4142135623730951}, "vertax.PbcMesh.from_seeds": {"tf": 1}, "vertax.PbcMesh.from_image": {"tf": 1.7320508075688772}, "vertax.PbcMesh.from_mask": {"tf": 1.7320508075688772}, "vertax.PbcMesh.create_empty": {"tf": 1}, "vertax.PbcMesh.copy_mesh": {"tf": 1.4142135623730951}, "vertax.PbcMesh.save_mesh": {"tf": 1}, "vertax.PbcMesh.load_mesh": {"tf": 2}, "vertax.PbcMesh.save_mesh_txt": {"tf": 1}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1.4142135623730951}, "vertax.BoundedMesh": {"tf": 1}, "vertax.BoundedMesh.nb_angles": {"tf": 1}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.from_seeds": {"tf": 1.7320508075688772}, "vertax.BoundedMesh.create_empty": {"tf": 1}, "vertax.BoundedMesh.copy_mesh": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.save_mesh": {"tf": 1}, "vertax.BoundedMesh.load_mesh": {"tf": 2}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1.4142135623730951}, "vertax.plot_mesh": {"tf": 1.4142135623730951}, "vertax.cost_mesh2image": {"tf": 1}, "vertax.cost_areas": {"tf": 1.4142135623730951}}, "df": 35, "e": {"docs": {}, "df": 0, "s": {"docs": {"vertax": {"tf": 1.7320508075688772}, "vertax.Mesh.__init__": {"tf": 1}, "vertax.PbcBilevelOptimizer": {"tf": 1}, "vertax.PbcBilevelOptimizer.__init__": {"tf": 1}, "vertax.BoundedBilevelOptimizer": {"tf": 1}, "vertax.BoundedBilevelOptimizer.__init__": {"tf": 1}, "vertax.energy_line_tensions": {"tf": 1}, "vertax.energy_line_tensions_bounded": {"tf": 1}}, "df": 8}}}}, "e": {"docs": {}, "df": 0, "t": {"docs": {"vertax": {"tf": 1}, "vertax.Mesh.vertices": {"tf": 1}}, "df": 2}}, "t": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {"vertax.BoundedMesh.from_seeds": {"tf": 1}, "vertax.BilevelOptimizationMethod": {"tf": 1}}, "df": 2, "s": {"docs": {"vertax": {"tf": 1}, "vertax.Mesh.__init__": {"tf": 1}, "vertax.PbcMesh.__init__": {"tf": 1}}, "df": 3}}}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "m": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}, "r": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"vertax.PbcMesh.from_image": {"tf": 1}, "vertax.PbcMesh.from_mask": {"tf": 1}}, "df": 2}}}}}}}, "g": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "t": {"docs": {"vertax.BoundedMesh.from_seeds": {"tf": 1}}, "df": 1}}}, "s": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"vertax.cost_d_IAS": {"tf": 1}}, "df": 1}}}}}}}}, "x": {"docs": {"vertax.cost_v2v_ias": {"tf": 1}, "vertax.cost_v2v_tem": {"tf": 1}}, "df": 2}}, "a": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {"vertax": {"tf": 1}, "vertax.PbcMesh.from_image": {"tf": 1}, "vertax.PbcMesh.from_mask": {"tf": 1}, "vertax.PbcMesh.update_boundary_conditions": {"tf": 1}}, "df": 4}}, "t": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "{": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "}": {"docs": {}, "df": 0, "\\": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "t": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}}}, "e": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}}, "c": {"docs": {}, "df": 0, "h": {"docs": {"vertax": {"tf": 1}}, "df": 1}}, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "b": {"docs": {"vertax.get_plot_mesh": {"tf": 1}}, "df": 1}}}}}}}}, "k": {"docs": {}, "df": 0, "e": {"docs": {"vertax": {"tf": 1}}, "df": 1}}, "s": {"docs": {}, "df": 0, "k": {"docs": {"vertax.PbcMesh.__init__": {"tf": 1}, "vertax.PbcMesh.from_image": {"tf": 1.7320508075688772}, "vertax.PbcMesh.from_mask": {"tf": 2}}, "df": 3}}, "x": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "m": {"docs": {"vertax.PbcMesh.MAX_EDGES_IN_ANY_FACE": {"tf": 1}}, "df": 1}}}}}, "r": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"vertax.BoundedMesh.from_random_seeds": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}, "vertax.FacePlot.FATES": {"tf": 1}}, "df": 3}}}}}, "u": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {"vertax.PbcMesh.MAX_EDGES_IN_ANY_FACE": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_inner_loss": {"tf": 1}}, "df": 3}}}}, "i": {"docs": {"vertax": {"tf": 1}, "vertax.cost_IAS": {"tf": 1.4142135623730951}, "vertax.cost_d_IAS": {"tf": 1.4142135623730951}}, "df": 3, "n": {"docs": {"vertax": {"tf": 2.8284271247461903}, "vertax.Mesh.width": {"tf": 1.4142135623730951}, "vertax.Mesh.height": {"tf": 1.4142135623730951}, "vertax.PbcMesh": {"tf": 1}, "vertax.PbcMesh.MAX_EDGES_IN_ANY_FACE": {"tf": 1}, "vertax.PbcMesh.from_random_seeds": {"tf": 1.4142135623730951}, "vertax.PbcMesh.copy_mesh": {"tf": 1}, "vertax.PbcMesh.save_mesh_txt": {"tf": 1.4142135623730951}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1}, "vertax.PbcMesh.get_length_with_offset": {"tf": 1}, "vertax.BoundedMesh": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.copy_mesh": {"tf": 1}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1}, "vertax.BilevelOptimizationMethod": {"tf": 1}}, "df": 16, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"vertax": {"tf": 1.7320508075688772}}, "df": 1}}}}}, "t": {"docs": {"vertax.PbcMesh.from_random_seeds": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1.7320508075688772}, "vertax.BoundedMesh.from_seeds": {"tf": 1.4142135623730951}}, "df": 3, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}}}, "f": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {"vertax": {"tf": 1}, "vertax.Mesh.edges": {"tf": 1}}, "df": 2, "s": {"docs": {"vertax": {"tf": 1}, "vertax.BoundedMesh": {"tf": 1}, "vertax.BoundedMesh.angles": {"tf": 1}, "vertax.BoundedMesh.nb_angles": {"tf": 1}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1}}, "df": 5}}}}}}, "g": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {"vertax.PbcMesh": {"tf": 1.4142135623730951}, "vertax.BoundedMesh": {"tf": 1.4142135623730951}}, "df": 2}}}}}}, "f": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}}}, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"vertax": {"tf": 1}, "vertax.BilevelOptimizationMethod.AUTOMATIC_DIFFERENTIATION": {"tf": 1}, "vertax.PbcBilevelOptimizer.__init__": {"tf": 1}, "vertax.PbcBilevelOptimizer.compute_inner_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.__init__": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_inner_loss": {"tf": 1}}, "df": 6}}}, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {"vertax.BoundedMesh": {"tf": 1.4142135623730951}}, "df": 1}}}, "p": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"vertax.cost_tem_halfedge": {"tf": 1}}, "df": 1}}}}}}, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "x": {"docs": {"vertax.cost_IAS": {"tf": 1}, "vertax.cost_d_IAS": {"tf": 1}}, "df": 2}}}}, "s": {"docs": {"vertax": {"tf": 2.449489742783178}, "vertax.Mesh": {"tf": 1}, "vertax.Mesh.save_mesh": {"tf": 1.4142135623730951}, "vertax.PbcMesh": {"tf": 1.7320508075688772}, "vertax.PbcMesh.MAX_EDGES_IN_ANY_FACE": {"tf": 1}, "vertax.PbcMesh.from_image": {"tf": 1}, "vertax.PbcMesh.from_mask": {"tf": 1}, "vertax.PbcMesh.save_mesh": {"tf": 1.4142135623730951}, "vertax.PbcMesh.load_mesh": {"tf": 1}, "vertax.BoundedMesh": {"tf": 2.8284271247461903}, "vertax.BoundedMesh.save_mesh": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.load_mesh": {"tf": 1}, "vertax.FacePlot": {"tf": 1}, "vertax.EdgePlot": {"tf": 1}, "vertax.VertexPlot": {"tf": 1}, "vertax.cost_checkerboard": {"tf": 1}, "vertax.energy_shape_factor_homo": {"tf": 1}}, "df": 17}, "t": {"docs": {"vertax": {"tf": 1.7320508075688772}, "vertax.Mesh": {"tf": 1.4142135623730951}, "vertax.Mesh.__init__": {"tf": 1}, "vertax.PbcMesh.from_image": {"tf": 1}, "vertax.PbcMesh.update_boundary_conditions": {"tf": 1.4142135623730951}, "vertax.FacePlot": {"tf": 1}, "vertax.EdgePlot": {"tf": 1}, "vertax.VertexPlot": {"tf": 1}, "vertax.energy_shape_factor_homo": {"tf": 1}}, "df": 9, "s": {"docs": {"vertax.PbcMesh.from_image": {"tf": 1}, "vertax.PbcMesh.from_mask": {"tf": 1}, "vertax.PbcMesh.get_length_with_offset": {"tf": 1}, "vertax.FacePlot.FACE_PARAMETER": {"tf": 1}, "vertax.FacePlot.AREA": {"tf": 1}, "vertax.FacePlot.PERIMETER": {"tf": 1}, "vertax.EdgePlot.EDGE_PARAMETER": {"tf": 1}, "vertax.EdgePlot.LENGTH": {"tf": 1}, "vertax.VertexPlot.VERTEX_PARAMETER": {"tf": 1}}, "df": 9}, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {"vertax.BilevelOptimizationMethod.AUTOMATIC_DIFFERENTIATION": {"tf": 1}}, "df": 1}}}}}}}}}, "f": {"docs": {"vertax": {"tf": 1}, "vertax.PbcMesh.from_image": {"tf": 1}, "vertax.PbcMesh.create_empty": {"tf": 1}, "vertax.BoundedMesh": {"tf": 2}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.create_empty": {"tf": 1}, "vertax.FacePlot.FATES": {"tf": 1}}, "df": 8}, "e": {"docs": {"vertax.Mesh.nb_half_edges": {"tf": 1}}, "df": 1}, "d": {"docs": {"vertax.PbcMesh": {"tf": 2.6457513110645907}, "vertax.BoundedMesh": {"tf": 3}}, "df": 2}, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {"vertax.PbcMesh.__init__": {"tf": 1}, "vertax.PbcMesh.from_image": {"tf": 1.7320508075688772}, "vertax.cost_mesh2image": {"tf": 1.4142135623730951}}, "df": 3}}}, "p": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"vertax.PbcMesh.from_image": {"tf": 1}}, "df": 1}}}}}}, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {"vertax.BilevelOptimizationMethod.IMPLICIT_DIFFERENTIATION": {"tf": 1}, "vertax.BilevelOptimizationMethod.ADJOINT_STATE": {"tf": 1}}, "df": 2}}}}, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"vertax.BoundedBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_inner_loss": {"tf": 1}}, "df": 2}}}}}}}}}}, "l": {"docs": {}, "df": 0, "l": {"docs": {"vertax.BilevelOptimizationMethod.IMPLICIT_DIFFERENTIATION": {"tf": 1}, "vertax.BilevelOptimizationMethod.ADJOINT_STATE": {"tf": 1}}, "df": 2}}, "a": {"docs": {}, "df": 0, "s": {"docs": {"vertax.cost_IAS": {"tf": 1}, "vertax.cost_d_IAS": {"tf": 1}, "vertax.cost_v2v_ias": {"tf": 1}}, "df": 3}}}, "o": {"docs": {}, "df": 0, "f": {"docs": {"vertax": {"tf": 2.8284271247461903}, "vertax.Mesh.faces": {"tf": 1}, "vertax.Mesh.width": {"tf": 1}, "vertax.Mesh.height": {"tf": 1}, "vertax.Mesh.nb_faces": {"tf": 1.4142135623730951}, "vertax.Mesh.nb_edges": {"tf": 1.4142135623730951}, "vertax.Mesh.nb_half_edges": {"tf": 1.7320508075688772}, "vertax.Mesh.nb_vertices": {"tf": 1.4142135623730951}, "vertax.PbcMesh": {"tf": 3.7416573867739413}, "vertax.PbcMesh.MAX_EDGES_IN_ANY_FACE": {"tf": 1}, "vertax.PbcMesh.from_random_seeds": {"tf": 1.7320508075688772}, "vertax.PbcMesh.from_seeds": {"tf": 2.23606797749979}, "vertax.PbcMesh.get_length": {"tf": 1}, "vertax.PbcMesh.get_length_with_offset": {"tf": 1}, "vertax.PbcMesh.get_perimeter": {"tf": 1}, "vertax.PbcMesh.get_area": {"tf": 1}, "vertax.BoundedMesh": {"tf": 4.358898943540674}, "vertax.BoundedMesh.nb_angles": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.from_random_seeds": {"tf": 2}, "vertax.BoundedMesh.from_seeds": {"tf": 2.449489742783178}, "vertax.BoundedMesh.get_length": {"tf": 1}, "vertax.BoundedMesh.get_perimeter": {"tf": 1}, "vertax.BoundedMesh.get_area": {"tf": 1}, "vertax.BilevelOptimizationMethod.AUTOMATIC_DIFFERENTIATION": {"tf": 1}, "vertax.PbcBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.PbcBilevelOptimizer.compute_inner_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_inner_loss": {"tf": 1}, "vertax.cost_v2v": {"tf": 1}, "vertax.cost_areas": {"tf": 1.4142135623730951}, "vertax.cost_v2v_ias": {"tf": 1}, "vertax.cost_v2v_tem": {"tf": 1}}, "df": 32, "f": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {"vertax.PbcMesh": {"tf": 1.4142135623730951}, "vertax.PbcMesh.get_length_with_offset": {"tf": 1.4142135623730951}}, "df": 2, "s": {"docs": {"vertax.PbcMesh.get_length_with_offset": {"tf": 1}}, "df": 1}}}}}}, "n": {"docs": {"vertax": {"tf": 1.4142135623730951}, "vertax.PbcMesh": {"tf": 1.7320508075688772}, "vertax.PbcMesh.from_random_seeds": {"tf": 1}, "vertax.PbcMesh.from_seeds": {"tf": 1}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.from_seeds": {"tf": 1}, "vertax.BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION": {"tf": 1}, "vertax.FacePlot": {"tf": 1}, "vertax.FacePlot.FACE_PARAMETER": {"tf": 1}, "vertax.FacePlot.AREA": {"tf": 1}, "vertax.FacePlot.PERIMETER": {"tf": 1}, "vertax.FacePlot.FATES": {"tf": 1}, "vertax.EdgePlot": {"tf": 1}, "vertax.EdgePlot.EDGE_PARAMETER": {"tf": 1}, "vertax.EdgePlot.LENGTH": {"tf": 1}, "vertax.VertexPlot": {"tf": 1}, "vertax.VertexPlot.VERTEX_PARAMETER": {"tf": 1}, "vertax.energy_shape_factor_hetero": {"tf": 1}, "vertax.energy_line_tensions": {"tf": 1}, "vertax.energy_line_tensions_bounded": {"tf": 1}}, "df": 20, "l": {"docs": {}, "df": 0, "y": {"docs": {"vertax": {"tf": 1}, "vertax.PbcMesh.save_mesh_txt": {"tf": 1}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1}, "vertax.energy_shape_factor_homo": {"tf": 1}}, "df": 6}}, "e": {"docs": {"vertax.get_plot_mesh": {"tf": 1}, "vertax.cost_ratio": {"tf": 1}, "vertax.energy_shape_factor_homo": {"tf": 1}}, "df": 3, "s": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}, "r": {"docs": {"vertax": {"tf": 2}, "vertax.PbcMesh": {"tf": 1.4142135623730951}, "vertax.BilevelOptimizationMethod.ADJOINT_STATE": {"tf": 1}, "vertax.plot_mesh": {"tf": 1}}, "df": 4, "t": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"vertax.cost_ratio": {"tf": 1}}, "df": 1}}}}}}}}}, "t": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"vertax": {"tf": 1.4142135623730951}, "vertax.PbcMesh.save_mesh_txt": {"tf": 1}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1}}, "df": 5, "w": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}}}}, "b": {"docs": {}, "df": 0, "j": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "s": {"docs": {"vertax": {"tf": 1}}, "df": 1}, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}}}}, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "z": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"vertax.PbcBilevelOptimizer": {"tf": 1}, "vertax.PbcBilevelOptimizer.__init__": {"tf": 1}, "vertax.BoundedBilevelOptimizer": {"tf": 1}, "vertax.BoundedBilevelOptimizer.__init__": {"tf": 1}}, "df": 4, "s": {"docs": {"vertax": {"tf": 1}}, "df": 1}}, "d": {"docs": {"vertax.Mesh.faces_params": {"tf": 1}, "vertax.Mesh.edges_params": {"tf": 1}, "vertax.Mesh.vertices_params": {"tf": 1}}, "df": 3}}, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"vertax": {"tf": 1.7320508075688772}, "vertax.PbcMesh.MAX_EDGES_IN_ANY_FACE": {"tf": 1}, "vertax.BilevelOptimizationMethod": {"tf": 1.4142135623730951}, "vertax.BilevelOptimizationMethod.AUTOMATIC_DIFFERENTIATION": {"tf": 1}}, "df": 4, "s": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}}}}, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "y": {"docs": {"vertax.BilevelOptimizationMethod.IMPLICIT_DIFFERENTIATION": {"tf": 1}, "vertax.BilevelOptimizationMethod.ADJOINT_STATE": {"tf": 1}}, "df": 2}}}}}}, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"vertax.PbcMesh.save_mesh_txt": {"tf": 2.6457513110645907}, "vertax.PbcMesh.load_mesh_txt": {"tf": 2.6457513110645907}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 2.8284271247461903}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 2.8284271247461903}}, "df": 4}}}}}}}, "w": {"docs": {}, "df": 0, "n": {"docs": {"vertax": {"tf": 1.7320508075688772}}, "df": 1}}, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"vertax": {"tf": 1}, "vertax.PbcBilevelOptimizer.__init__": {"tf": 1}, "vertax.PbcBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.__init__": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_outer_loss": {"tf": 1}}, "df": 5}}, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {"vertax.BoundedMesh": {"tf": 1.4142135623730951}}, "df": 1}}}}}, "r": {"docs": {"vertax.BoundedMesh.from_seeds": {"tf": 1}}, "df": 1}}}, "e": {"1": {"docs": {"vertax.energy_shape_factor_hetero": {"tf": 1}, "vertax.energy_shape_factor_homo": {"tf": 1}}, "df": 2}, "2": {"docs": {"vertax.energy_line_tensions": {"tf": 1}, "vertax.energy_line_tensions_bounded": {"tf": 1}}, "df": 2}, "docs": {"vertax": {"tf": 1}, "vertax.BilevelOptimizationMethod.IMPLICIT_DIFFERENTIATION": {"tf": 1}, "vertax.BilevelOptimizationMethod.ADJOINT_STATE": {"tf": 1}}, "df": 3, "p": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"vertax": {"tf": 2}}, "df": 1}}}}}}}}}, "d": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {"vertax.PbcMesh": {"tf": 2.23606797749979}, "vertax.BoundedMesh": {"tf": 3.605551275463989}, "vertax.BoundedMesh.get_length": {"tf": 1}, "vertax.EdgePlot": {"tf": 1}, "vertax.EdgePlot.EDGE_PARAMETER": {"tf": 1}, "vertax.EdgePlot.LENGTH": {"tf": 1}, "vertax.cost_tem_halfedge": {"tf": 1}, "vertax.energy_line_tensions": {"tf": 1}, "vertax.energy_line_tensions_bounded": {"tf": 1}}, "df": 9, "s": {"docs": {"vertax": {"tf": 1.4142135623730951}, "vertax.Mesh.edges": {"tf": 1}, "vertax.Mesh.edges_params": {"tf": 1}, "vertax.Mesh.nb_edges": {"tf": 1}, "vertax.Mesh.nb_half_edges": {"tf": 1.4142135623730951}, "vertax.PbcMesh": {"tf": 1.4142135623730951}, "vertax.PbcMesh.MAX_EDGES_IN_ANY_FACE": {"tf": 1}, "vertax.PbcMesh.from_image": {"tf": 1}, "vertax.PbcMesh.from_mask": {"tf": 1}, "vertax.PbcMesh.save_mesh_txt": {"tf": 2.449489742783178}, "vertax.PbcMesh.load_mesh_txt": {"tf": 2.449489742783178}, "vertax.PbcMesh.get_length": {"tf": 1}, "vertax.PbcMesh.get_length_with_offset": {"tf": 1}, "vertax.BoundedMesh": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 2.23606797749979}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 2.449489742783178}, "vertax.EdgePlot.BLACK": {"tf": 1}, "vertax.EdgePlot.INVISIBLE": {"tf": 1}, "vertax.cost_d_IAS": {"tf": 1}}, "df": 19}}}}, "q": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "m": {"docs": {"vertax": {"tf": 1.7320508075688772}}, "df": 1}}, "a": {"docs": {"vertax.BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION": {"tf": 1}}, "df": 1}}}}}}}}}, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "y": {"docs": {"vertax": {"tf": 2}, "vertax.energy_shape_factor_hetero": {"tf": 1}, "vertax.energy_shape_factor_homo": {"tf": 1}, "vertax.energy_line_tensions": {"tf": 1}, "vertax.energy_line_tensions_bounded": {"tf": 1}}, "df": 5}}}}, "d": {"docs": {}, "df": 0, "{": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"vertax": {"tf": 1.4142135623730951}}, "df": 1}}}}}}}}}}, "x": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "s": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}}}}}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"vertax.Mesh.save_mesh": {"tf": 1}, "vertax.PbcMesh.save_mesh": {"tf": 1}, "vertax.BoundedMesh.save_mesh": {"tf": 1}}, "df": 3}}}}}}}, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"vertax": {"tf": 1.4142135623730951}}, "df": 1, "s": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}, "c": {"docs": {}, "df": 0, "t": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {"vertax": {"tf": 1.4142135623730951}}, "df": 1}}}}, "c": {"docs": {}, "df": 0, "h": {"docs": {"vertax.FacePlot.MULTICOLOR": {"tf": 1}}, "df": 1}}}, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"vertax.PbcMesh": {"tf": 1.4142135623730951}}, "df": 1}}}}}, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "y": {"docs": {"vertax.PbcMesh.__init__": {"tf": 1}, "vertax.PbcMesh.create_empty": {"tf": 1}, "vertax.BoundedMesh.create_empty": {"tf": 1}}, "df": 3}}}}, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"vertax.PbcMesh.MAX_EDGES_IN_ANY_FACE": {"tf": 1}}, "df": 1}, "s": {"docs": {"vertax.BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION": {"tf": 1}}, "df": 1}}}}}}}}, "l": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"vertax.BoundedMesh": {"tf": 2}}, "df": 1}}, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"vertax.cost_ratio": {"tf": 1}}, "df": 1}}}}}}, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {"vertax.energy_line_tensions": {"tf": 1}, "vertax.energy_line_tensions_bounded": {"tf": 1}}, "df": 2}}}}}}, "f": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"vertax.BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION": {"tf": 1}}, "df": 1}}}}}}}}}, "t": {"docs": {"vertax": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.from_seeds": {"tf": 1}}, "df": 2, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "e": {"docs": {"vertax": {"tf": 2.23606797749979}, "vertax.Mesh.faces": {"tf": 1}}, "df": 2, "s": {"docs": {"vertax": {"tf": 1.7320508075688772}}, "df": 1}}}}}}, "h": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "h": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}, "e": {"docs": {"vertax": {"tf": 3.872983346207417}, "vertax.Mesh.faces": {"tf": 1.4142135623730951}, "vertax.Mesh.edges": {"tf": 1}, "vertax.Mesh.vertices": {"tf": 1}, "vertax.Mesh.width": {"tf": 1.4142135623730951}, "vertax.Mesh.height": {"tf": 1.4142135623730951}, "vertax.Mesh.nb_faces": {"tf": 1.4142135623730951}, "vertax.Mesh.nb_edges": {"tf": 1.4142135623730951}, "vertax.Mesh.nb_half_edges": {"tf": 1.7320508075688772}, "vertax.Mesh.nb_vertices": {"tf": 1.4142135623730951}, "vertax.Mesh.save_mesh": {"tf": 1.4142135623730951}, "vertax.Mesh.load_mesh": {"tf": 1.7320508075688772}, "vertax.PbcMesh": {"tf": 2.6457513110645907}, "vertax.PbcMesh.__init__": {"tf": 1.4142135623730951}, "vertax.PbcMesh.MAX_EDGES_IN_ANY_FACE": {"tf": 1}, "vertax.PbcMesh.from_random_seeds": {"tf": 2.449489742783178}, "vertax.PbcMesh.from_seeds": {"tf": 2.23606797749979}, "vertax.PbcMesh.from_image": {"tf": 2.6457513110645907}, "vertax.PbcMesh.from_mask": {"tf": 2}, "vertax.PbcMesh.save_mesh": {"tf": 1.4142135623730951}, "vertax.PbcMesh.load_mesh": {"tf": 1.7320508075688772}, "vertax.PbcMesh.save_mesh_txt": {"tf": 3}, "vertax.PbcMesh.load_mesh_txt": {"tf": 3}, "vertax.PbcMesh.get_length": {"tf": 1}, "vertax.PbcMesh.get_length_with_offset": {"tf": 1}, "vertax.PbcMesh.get_perimeter": {"tf": 1}, "vertax.PbcMesh.get_area": {"tf": 1}, "vertax.BoundedMesh": {"tf": 2.8284271247461903}, "vertax.BoundedMesh.__init__": {"tf": 1}, "vertax.BoundedMesh.angles": {"tf": 1}, "vertax.BoundedMesh.nb_angles": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.from_random_seeds": {"tf": 2.23606797749979}, "vertax.BoundedMesh.from_seeds": {"tf": 2.449489742783178}, "vertax.BoundedMesh.save_mesh": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.load_mesh": {"tf": 1.7320508075688772}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 3.1622776601683795}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 3.1622776601683795}, "vertax.BoundedMesh.get_length": {"tf": 1}, "vertax.BoundedMesh.get_perimeter": {"tf": 1}, "vertax.BoundedMesh.get_area": {"tf": 1}, "vertax.BilevelOptimizationMethod": {"tf": 1}, "vertax.BilevelOptimizationMethod.AUTOMATIC_DIFFERENTIATION": {"tf": 1.4142135623730951}, "vertax.BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION": {"tf": 1.4142135623730951}, "vertax.BilevelOptimizationMethod.IMPLICIT_DIFFERENTIATION": {"tf": 1}, "vertax.BilevelOptimizationMethod.ADJOINT_STATE": {"tf": 1}, "vertax.PbcBilevelOptimizer.__init__": {"tf": 1}, "vertax.PbcBilevelOptimizer.compute_outer_loss": {"tf": 1.4142135623730951}, "vertax.PbcBilevelOptimizer.compute_inner_loss": {"tf": 1.4142135623730951}, "vertax.BoundedBilevelOptimizer.__init__": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_outer_loss": {"tf": 1.4142135623730951}, "vertax.BoundedBilevelOptimizer.compute_inner_loss": {"tf": 1.4142135623730951}, "vertax.plot_mesh": {"tf": 1.4142135623730951}, "vertax.get_plot_mesh": {"tf": 1}, "vertax.FacePlot.FACE_PARAMETER": {"tf": 1}, "vertax.FacePlot.AREA": {"tf": 1}, "vertax.FacePlot.PERIMETER": {"tf": 1}, "vertax.EdgePlot.EDGE_PARAMETER": {"tf": 1}, "vertax.EdgePlot.LENGTH": {"tf": 1}, "vertax.VertexPlot.VERTEX_PARAMETER": {"tf": 1}, "vertax.cost_v2v": {"tf": 1}, "vertax.cost_mesh2image": {"tf": 1}, "vertax.cost_areas": {"tf": 2}, "vertax.cost_IAS": {"tf": 1}, "vertax.cost_ratio": {"tf": 1}, "vertax.energy_shape_factor_hetero": {"tf": 1.4142135623730951}, "vertax.energy_shape_factor_homo": {"tf": 1}}, "df": 66, "s": {"docs": {}, "df": 0, "e": {"docs": {"vertax": {"tf": 1.4142135623730951}}, "df": 1}}, "y": {"docs": {"vertax": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}}, "df": 2}, "t": {"docs": {}, "df": 0, "a": {"docs": {"vertax": {"tf": 2.449489742783178}}, "df": 1, "^": {"docs": {}, "df": 0, "{": {"docs": {}, "df": 0, "\\": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}}, "\\": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "t": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}}}}, "n": {"docs": {"vertax": {"tf": 1}}, "df": 1}, "r": {"docs": {}, "df": 0, "e": {"docs": {"vertax": {"tf": 1}, "vertax.cost_checkerboard": {"tf": 1}}, "df": 2}}, "i": {"docs": {}, "df": 0, "r": {"docs": {"vertax": {"tf": 1}, "vertax.FacePlot.FATES": {"tf": 1}}, "df": 2}}, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "m": {"docs": {"vertax.BilevelOptimizationMethod.IMPLICIT_DIFFERENTIATION": {"tf": 1}, "vertax.BilevelOptimizationMethod.ADJOINT_STATE": {"tf": 1}}, "df": 2}}}}}, "i": {"docs": {}, "df": 0, "s": {"docs": {"vertax": {"tf": 1.4142135623730951}, "vertax.Mesh.__init__": {"tf": 1}, "vertax.PbcMesh": {"tf": 1}, "vertax.BoundedMesh": {"tf": 1}, "vertax.cost_checkerboard": {"tf": 1}}, "df": 5}}, "o": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}, "a": {"docs": {}, "df": 0, "n": {"docs": {"vertax.PbcMesh.MAX_EDGES_IN_ANY_FACE": {"tf": 1}}, "df": 1}, "t": {"docs": {"vertax.PbcMesh.from_image": {"tf": 1}, "vertax.PbcMesh.save_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1}, "vertax.cost_ratio": {"tf": 1}, "vertax.cost_checkerboard": {"tf": 1}}, "df": 6}}}, "o": {"docs": {"vertax": {"tf": 2.8284271247461903}, "vertax.Mesh": {"tf": 1}, "vertax.Mesh.__init__": {"tf": 1}, "vertax.Mesh.faces_params": {"tf": 1}, "vertax.Mesh.edges_params": {"tf": 1}, "vertax.Mesh.vertices_params": {"tf": 1}, "vertax.Mesh.save_mesh": {"tf": 1.4142135623730951}, "vertax.Mesh.load_mesh": {"tf": 1}, "vertax.PbcMesh": {"tf": 1}, "vertax.PbcMesh.from_random_seeds": {"tf": 1}, "vertax.PbcMesh.from_seeds": {"tf": 1}, "vertax.PbcMesh.from_image": {"tf": 1.4142135623730951}, "vertax.PbcMesh.save_mesh": {"tf": 1.4142135623730951}, "vertax.PbcMesh.load_mesh": {"tf": 1}, "vertax.PbcMesh.save_mesh_txt": {"tf": 3}, "vertax.PbcMesh.load_mesh_txt": {"tf": 2.6457513110645907}, "vertax.PbcMesh.update_boundary_conditions": {"tf": 1}, "vertax.BoundedMesh": {"tf": 1}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.from_seeds": {"tf": 1.7320508075688772}, "vertax.BoundedMesh.save_mesh": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.load_mesh": {"tf": 1}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 3.1622776601683795}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 2.8284271247461903}, "vertax.BilevelOptimizationMethod": {"tf": 1}, "vertax.BilevelOptimizationMethod.IMPLICIT_DIFFERENTIATION": {"tf": 1}, "vertax.BilevelOptimizationMethod.ADJOINT_STATE": {"tf": 1}, "vertax.plot_mesh": {"tf": 1}, "vertax.FacePlot": {"tf": 1}, "vertax.EdgePlot": {"tf": 1}, "vertax.VertexPlot": {"tf": 1}, "vertax.cost_v2v": {"tf": 1.4142135623730951}, "vertax.cost_mesh2image": {"tf": 1.4142135623730951}, "vertax.cost_IAS": {"tf": 1}, "vertax.cost_ratio": {"tf": 1}, "vertax.cost_checkerboard": {"tf": 1.4142135623730951}}, "df": 36, "p": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "y": {"docs": {"vertax.cost_IAS": {"tf": 1}}, "df": 1}}}}}}}, "w": {"docs": {}, "df": 0, "o": {"docs": {"vertax": {"tf": 1.4142135623730951}}, "df": 1}, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {"vertax.Mesh.nb_half_edges": {"tf": 1}}, "df": 1}}, "n": {"docs": {"vertax.PbcMesh": {"tf": 1}, "vertax.BoundedMesh": {"tf": 1}}, "df": 2}}}, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}}}}}, "y": {"docs": {"vertax.PbcMesh.from_image": {"tf": 1}}, "df": 1}}, "y": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"vertax": {"tf": 1}}, "df": 1}}, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}}, "e": {"docs": {}, "df": 0, "x": {"docs": {}, "df": 0, "t": {"docs": {"vertax.PbcMesh.save_mesh_txt": {"tf": 1}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1.4142135623730951}}, "df": 4, "b": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "{": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}}}}, "{": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}, "s": {"docs": {"vertax": {"tf": 1}}, "df": 1}, "c": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}}}}}}, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"vertax.energy_line_tensions": {"tf": 1}, "vertax.energy_line_tensions_bounded": {"tf": 1}}, "df": 2, "s": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}}, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {"vertax.Mesh.__init__": {"tf": 1}, "vertax.Mesh.edges": {"tf": 1}}, "df": 2}}}}}}}}}, "m": {"docs": {"vertax.cost_tem_halfedge": {"tf": 1}, "vertax.cost_v2v_tem": {"tf": 1}}, "df": 2, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"vertax.PbcMesh.from_image": {"tf": 1}, "vertax.PbcMesh.from_mask": {"tf": 1}}, "df": 2}}}}}}, "r": {"docs": {}, "df": 0, "m": {"docs": {"vertax.energy_line_tensions": {"tf": 1}, "vertax.energy_line_tensions_bounded": {"tf": 1}}, "df": 2}}}, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {"vertax": {"tf": 1.4142135623730951}, "vertax.PbcMesh": {"tf": 1.7320508075688772}, "vertax.BoundedMesh": {"tf": 1.4142135623730951}, "vertax.cost_v2v": {"tf": 1}, "vertax.cost_mesh2image": {"tf": 1}, "vertax.cost_areas": {"tf": 1}}, "df": 6}}}}, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"vertax.PbcMesh.save_mesh_txt": {"tf": 2.449489742783178}, "vertax.PbcMesh.load_mesh_txt": {"tf": 2.449489742783178}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 2.6457513110645907}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 2.6457513110645907}}, "df": 4}}}}, "x": {"docs": {}, "df": 0, "t": {"docs": {"vertax.PbcMesh.save_mesh_txt": {"tf": 2.6457513110645907}, "vertax.PbcMesh.load_mesh_txt": {"tf": 2.6457513110645907}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 2.8284271247461903}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 2.8284271247461903}}, "df": 4}}}, "r": {"docs": {}, "df": 0, "e": {"docs": {"vertax.PbcMesh.create_empty": {"tf": 1}, "vertax.BoundedMesh.create_empty": {"tf": 1}}, "df": 2, "s": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "t": {"docs": {"vertax.PbcMesh.from_image": {"tf": 1}, "vertax.PbcBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.PbcBilevelOptimizer.compute_inner_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_inner_loss": {"tf": 1}}, "df": 5, "s": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}, "p": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"vertax.cost_IAS": {"tf": 1}}, "df": 1, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {"vertax.cost_v2v_tem": {"tf": 1}}, "df": 1}}}}}}}}, "p": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "s": {"docs": {"vertax": {"tf": 1.4142135623730951}}, "df": 1}}}}}}, "o": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "y": {"docs": {"vertax.PbcMesh.from_random_seeds": {"tf": 1}}, "df": 1}}}}}}}}}}}}, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}}}, "o": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "y": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}}}}, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "h": {"docs": {"vertax": {"tf": 1}}, "df": 1}}, "d": {"docs": {"vertax.PbcMesh.save_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1}}, "df": 2}}, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"vertax.Mesh.width": {"tf": 1}, "vertax.Mesh.height": {"tf": 1}}, "df": 2}}, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {"vertax.PbcMesh.from_random_seeds": {"tf": 1.4142135623730951}}, "df": 1}}}}}}}}}, "t": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {"vertax.Mesh.load_mesh": {"tf": 1}, "vertax.PbcMesh.from_random_seeds": {"tf": 1}, "vertax.PbcMesh.from_image": {"tf": 1}, "vertax.PbcMesh.from_mask": {"tf": 1}, "vertax.PbcMesh.load_mesh": {"tf": 1}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1}, "vertax.BoundedMesh.load_mesh": {"tf": 1}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1}}, "df": 9}}}}}, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"vertax.PbcMesh.load_mesh": {"tf": 1}, "vertax.BoundedMesh.load_mesh": {"tf": 1}}, "df": 2}}}}}}, "q": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"vertax.BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION": {"tf": 1}}, "df": 1}, "s": {"docs": {"vertax.BilevelOptimizationMethod.IMPLICIT_DIFFERENTIATION": {"tf": 1}, "vertax.BilevelOptimizationMethod.ADJOINT_STATE": {"tf": 1}}, "df": 2}}}}}}}, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "m": {"docs": {"vertax.PbcMesh.__init__": {"tf": 1}, "vertax.PbcMesh.from_random_seeds": {"tf": 2}, "vertax.BoundedMesh.from_random_seeds": {"tf": 2}, "vertax.BoundedMesh.from_seeds": {"tf": 1.4142135623730951}, "vertax.FacePlot.MULTICOLOR": {"tf": 1}}, "df": 5}}}}}, "u": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "y": {"docs": {"vertax.PbcMesh.from_image": {"tf": 1}, "vertax.PbcMesh.from_mask": {"tf": 1}}, "df": 2}}}}}}}}}}}, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}, "a": {"docs": {}, "df": 0, "d": {"docs": {"vertax.Mesh.load_mesh": {"tf": 1}, "vertax.PbcMesh.load_mesh": {"tf": 1}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.load_mesh": {"tf": 1}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1.4142135623730951}}, "df": 5, "e": {"docs": {}, "df": 0, "d": {"docs": {"vertax.Mesh.load_mesh": {"tf": 1}, "vertax.PbcMesh.load_mesh": {"tf": 1}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.load_mesh": {"tf": 1}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1}}, "df": 5}}}}, "s": {"docs": {}, "df": 0, "s": {"docs": {"vertax.PbcBilevelOptimizer.__init__": {"tf": 1}, "vertax.PbcBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.PbcBilevelOptimizer.compute_inner_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.__init__": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_inner_loss": {"tf": 1}, "vertax.cost_IAS": {"tf": 1}, "vertax.cost_d_IAS": {"tf": 1}, "vertax.cost_tem_halfedge": {"tf": 1}}, "df": 9}}}, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}, "d": {"docs": {}, "df": 0, "s": {"docs": {"vertax.cost_checkerboard": {"tf": 1}}, "df": 1}}}, "f": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "w": {"docs": {"vertax": {"tf": 1.4142135623730951}}, "df": 1}}}}}}}, "n": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"vertax.PbcMesh.get_length": {"tf": 1}, "vertax.PbcMesh.get_length_with_offset": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.get_length": {"tf": 1}, "vertax.EdgePlot.LENGTH": {"tf": 1}}, "df": 4, "s": {"docs": {"vertax.energy_line_tensions": {"tf": 1}, "vertax.energy_line_tensions_bounded": {"tf": 1}}, "df": 2}}}}}, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {"vertax.BilevelOptimizationMethod": {"tf": 1}, "vertax.PbcBilevelOptimizer": {"tf": 1}, "vertax.PbcBilevelOptimizer.__init__": {"tf": 1}, "vertax.BoundedBilevelOptimizer": {"tf": 1}, "vertax.BoundedBilevelOptimizer.__init__": {"tf": 1}}, "df": 5}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {"vertax": {"tf": 1}, "vertax.energy_line_tensions": {"tf": 1}, "vertax.energy_line_tensions_bounded": {"tf": 1}}, "df": 3}}, "v": {"docs": {}, "df": 0, "e": {"docs": {"vertax.Mesh.width": {"tf": 1}, "vertax.Mesh.height": {"tf": 1}}, "df": 2}}, "s": {"docs": {}, "df": 0, "t": {"docs": {"vertax.PbcMesh.from_seeds": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}}, "df": 2}}, "k": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {"vertax.PbcMesh.update_boundary_conditions": {"tf": 1}}, "df": 1}}}}}, "l": {"docs": {"vertax.PbcMesh.update_boundary_conditions": {"tf": 1}}, "df": 1}}, "c": {"docs": {"vertax.cost_IAS": {"tf": 1}, "vertax.cost_d_IAS": {"tf": 1}}, "df": 2, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {"vertax.BoundedMesh.from_random_seeds": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.from_seeds": {"tf": 1.4142135623730951}, "vertax.energy_shape_factor_hetero": {"tf": 1}, "vertax.energy_line_tensions": {"tf": 1}, "vertax.energy_line_tensions_bounded": {"tf": 1}}, "df": 5, "s": {"docs": {"vertax": {"tf": 2.23606797749979}, "vertax.Mesh.faces": {"tf": 1}, "vertax.Mesh.edges": {"tf": 1}, "vertax.Mesh.vertices": {"tf": 1}, "vertax.BoundedMesh": {"tf": 1}, "vertax.cost_tem_halfedge": {"tf": 1}, "vertax.cost_checkerboard": {"tf": 1.4142135623730951}}, "df": 7}, "p": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"vertax.PbcMesh.from_image": {"tf": 1}}, "df": 1}}}}}}, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}}, "h": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {"vertax": {"tf": 1.4142135623730951}}, "df": 1}}}}}}}, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "d": {"docs": {"vertax.BoundedBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_inner_loss": {"tf": 1}}, "df": 2}}}, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "d": {"docs": {"vertax.cost_checkerboard": {"tf": 1}}, "df": 1}}}}}}}}}}}, "o": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}}}}}, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"vertax.cost_v2v": {"tf": 1}, "vertax.cost_mesh2image": {"tf": 1}, "vertax.cost_areas": {"tf": 1}}, "df": 3}}}}, "m": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"vertax": {"tf": 1}, "vertax.Mesh": {"tf": 1}}, "df": 2}}}, "e": {"docs": {}, "df": 0, "s": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "s": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"vertax.PbcMesh": {"tf": 1.4142135623730951}, "vertax.PbcMesh.from_seeds": {"tf": 1.4142135623730951}, "vertax.BoundedMesh": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.from_seeds": {"tf": 1.4142135623730951}}, "df": 5}}}}}}}, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"vertax.PbcMesh": {"tf": 1}, "vertax.BilevelOptimizationMethod.IMPLICIT_DIFFERENTIATION": {"tf": 1}, "vertax.BilevelOptimizationMethod.ADJOINT_STATE": {"tf": 1}, "vertax.PbcBilevelOptimizer": {"tf": 1}, "vertax.PbcBilevelOptimizer.__init__": {"tf": 1}}, "df": 5, "s": {"docs": {"vertax": {"tf": 1}, "vertax.PbcMesh.from_random_seeds": {"tf": 1}, "vertax.PbcMesh.from_seeds": {"tf": 1}, "vertax.PbcMesh.from_image": {"tf": 1}, "vertax.PbcMesh.from_mask": {"tf": 1}, "vertax.PbcMesh.update_boundary_conditions": {"tf": 1}}, "df": 6}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"vertax.BilevelOptimizationMethod.IMPLICIT_DIFFERENTIATION": {"tf": 1}, "vertax.BilevelOptimizationMethod.ADJOINT_STATE": {"tf": 1}}, "df": 2}}}}}}}}}, "f": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}}}}}}, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {"vertax.PbcMesh.__init__": {"tf": null}, "vertax.BoundedMesh.__init__": {"tf": null}}, "df": 2}}}}}}, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "s": {"docs": {"vertax.PbcMesh.save_mesh_txt": {"tf": 1.4142135623730951}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1.4142135623730951}}, "df": 4}}}}}}}, "s": {"docs": {}, "df": 0, "t": {"docs": {"vertax": {"tf": 1.7320508075688772}, "vertax.BilevelOptimizationMethod.AUTOMATIC_DIFFERENTIATION": {"tf": 1}, "vertax.cost_v2v": {"tf": 1}, "vertax.cost_mesh2image": {"tf": 1}, "vertax.cost_areas": {"tf": 1}, "vertax.cost_v2v_ias": {"tf": 1.4142135623730951}, "vertax.cost_v2v_tem": {"tf": 1.4142135623730951}, "vertax.cost_ratio": {"tf": 1}, "vertax.cost_checkerboard": {"tf": 1}}, "df": 9}}, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"vertax.BoundedMesh.from_seeds": {"tf": 1.4142135623730951}}, "df": 1, "s": {"docs": {"vertax.PbcMesh": {"tf": 1}, "vertax.BoundedMesh": {"tf": 1}}, "df": 2}}}}}}}}}, "r": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"vertax.PbcMesh.from_random_seeds": {"tf": 1}, "vertax.PbcMesh.from_image": {"tf": 1}, "vertax.PbcMesh.from_mask": {"tf": 1}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1}}, "df": 4}}}}}}}}, "c": {"docs": {}, "df": 0, "t": {"docs": {"vertax.BoundedMesh.from_seeds": {"tf": 1}, "vertax.PbcBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.PbcBilevelOptimizer.compute_inner_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_inner_loss": {"tf": 1}}, "df": 5}}}}}, "p": {"docs": {}, "df": 0, "y": {"docs": {"vertax.PbcMesh.copy_mesh": {"tf": 1}, "vertax.BoundedMesh.copy_mesh": {"tf": 1}}, "df": 2}}, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {"vertax.FacePlot.MULTICOLOR": {"tf": 1}, "vertax.FacePlot.FACE_PARAMETER": {"tf": 1}, "vertax.FacePlot.AREA": {"tf": 1}, "vertax.FacePlot.PERIMETER": {"tf": 1}, "vertax.EdgePlot.EDGE_PARAMETER": {"tf": 1}, "vertax.EdgePlot.LENGTH": {"tf": 1}, "vertax.VertexPlot.VERTEX_PARAMETER": {"tf": 1}}, "df": 7, "e": {"docs": {}, "df": 0, "d": {"docs": {"vertax.FacePlot.FATES": {"tf": 1}}, "df": 1}}}}}, "u": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "s": {"docs": {"vertax.cost_d_IAS": {"tf": 1}}, "df": 1}}}}}, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"vertax.BoundedMesh": {"tf": 2}}, "df": 1, "l": {"docs": {}, "df": 0, "y": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}}, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}, "l": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}}, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {"vertax": {"tf": 1.4142135623730951}, "vertax.Mesh": {"tf": 1}, "vertax.Mesh.__init__": {"tf": 1}, "vertax.PbcMesh.__init__": {"tf": 1}}, "df": 4, "e": {"docs": {}, "df": 0, "s": {"docs": {"vertax": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_inner_loss": {"tf": 1}}, "df": 3}}}}}}, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {"vertax.Mesh.__init__": {"tf": 1.4142135623730951}, "vertax.PbcMesh.__init__": {"tf": 1}, "vertax.BoundedMesh.__init__": {"tf": 1}}, "df": 3, "e": {"docs": {}, "df": 0, "d": {"docs": {"vertax": {"tf": 1}, "vertax.PbcBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.PbcBilevelOptimizer.compute_inner_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_inner_loss": {"tf": 1}}, "df": 5}}}}, "n": {"docs": {"vertax": {"tf": 1.4142135623730951}, "vertax.Mesh.faces_params": {"tf": 1}, "vertax.Mesh.edges_params": {"tf": 1}, "vertax.Mesh.vertices_params": {"tf": 1}, "vertax.PbcMesh.save_mesh_txt": {"tf": 1}, "vertax.PbcMesh.update_boundary_conditions": {"tf": 1}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1}}, "df": 7}}, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"vertax.Mesh.__init__": {"tf": 1.4142135623730951}, "vertax.PbcMesh.__init__": {"tf": 1}, "vertax.PbcMesh.from_random_seeds": {"tf": 1}, "vertax.PbcMesh.from_seeds": {"tf": 1}, "vertax.PbcMesh.from_image": {"tf": 1}, "vertax.PbcMesh.from_mask": {"tf": 1}, "vertax.PbcMesh.create_empty": {"tf": 1}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.create_empty": {"tf": 1}, "vertax.PbcBilevelOptimizer.__init__": {"tf": 1}, "vertax.BoundedBilevelOptimizer.__init__": {"tf": 1}}, "df": 12}}}}, "o": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"vertax.PbcMesh": {"tf": 1.4142135623730951}}, "df": 1}}}}}}}, "i": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"vertax.BoundedMesh": {"tf": 1}}, "df": 1}}}}}}}, "u": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}}}}}}}, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "s": {"docs": {"vertax.BilevelOptimizationMethod.AUTOMATIC_DIFFERENTIATION": {"tf": 1}}, "df": 1}}}}}, "i": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "m": {"docs": {"vertax.energy_shape_factor_homo": {"tf": 1}}, "df": 1}}}}}}, "s": {"docs": {}, "df": 0, "e": {"docs": {"vertax": {"tf": 2}, "vertax.PbcMesh.__init__": {"tf": 1}, "vertax.PbcMesh.from_random_seeds": {"tf": 1}, "vertax.PbcMesh.create_empty": {"tf": 1}, "vertax.PbcMesh.update_boundary_conditions": {"tf": 1}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}, "vertax.BoundedMesh.create_empty": {"tf": 1}, "vertax.BilevelOptimizationMethod": {"tf": 1}}, "df": 9, "r": {"docs": {"vertax": {"tf": 1}}, "df": 1, "s": {"docs": {"vertax": {"tf": 1}}, "df": 1}}, "d": {"docs": {"vertax": {"tf": 1}, "vertax.Mesh": {"tf": 1}}, "df": 2}, "s": {"docs": {"vertax.Mesh.__init__": {"tf": 1}}, "df": 1}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"vertax.PbcMesh.from_image": {"tf": 1}, "vertax.cost_tem_halfedge": {"tf": 1}}, "df": 2}}}}, "p": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"vertax.PbcMesh.update_boundary_conditions": {"tf": 1}}, "df": 1, "s": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}}}, "p": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}}}, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {"vertax": {"tf": 1}, "vertax.PbcMesh.from_seeds": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.from_seeds": {"tf": 1}, "vertax.cost_v2v": {"tf": 1}, "vertax.cost_mesh2image": {"tf": 1}}, "df": 5}}}, "v": {"docs": {}, "df": 0, "e": {"docs": {"vertax.PbcMesh.from_seeds": {"tf": 1}}, "df": 1}}}}}, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"vertax.BoundedMesh.from_random_seeds": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}, "vertax.FacePlot": {"tf": 1}, "vertax.EdgePlot": {"tf": 1}, "vertax.VertexPlot": {"tf": 1}}, "df": 5}}}}}}}, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "m": {"docs": {"vertax": {"tf": 1.7320508075688772}}, "df": 1, "s": {"docs": {"vertax": {"tf": 1.4142135623730951}}, "df": 1}}}}, "a": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {"vertax.PbcMesh.from_image": {"tf": 1}}, "df": 1}}}}}, "v": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {"vertax": {"tf": 1.4142135623730951}, "vertax.PbcMesh.from_image": {"tf": 1}}, "df": 2}}}}}, "e": {"docs": {"vertax": {"tf": 1}}, "df": 1, "v": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "s": {"docs": {"vertax.PbcMesh": {"tf": 1}, "vertax.BoundedMesh": {"tf": 1}}, "df": 2}}}}}}}, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"vertax": {"tf": 1.4142135623730951}, "vertax.PbcMesh.MAX_EDGES_IN_ANY_FACE": {"tf": 1}, "vertax.FacePlot.FACE_PARAMETER": {"tf": 1}, "vertax.EdgePlot.EDGE_PARAMETER": {"tf": 1}, "vertax.VertexPlot.VERTEX_PARAMETER": {"tf": 1}}, "df": 5, "s": {"docs": {"vertax": {"tf": 1.7320508075688772}, "vertax.Mesh.faces_params": {"tf": 1}, "vertax.Mesh.edges_params": {"tf": 1}, "vertax.Mesh.vertices_params": {"tf": 1}, "vertax.PbcMesh.copy_mesh": {"tf": 1}, "vertax.PbcMesh.save_mesh_txt": {"tf": 2}, "vertax.PbcMesh.load_mesh_txt": {"tf": 2}, "vertax.BoundedMesh.copy_mesh": {"tf": 1}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 2}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 2}, "vertax.BilevelOptimizationMethod.AUTOMATIC_DIFFERENTIATION": {"tf": 1}, "vertax.PbcBilevelOptimizer.__init__": {"tf": 1}, "vertax.BoundedBilevelOptimizer.__init__": {"tf": 1}}, "df": 13}}}}}, "s": {"docs": {"vertax.PbcMesh.save_mesh_txt": {"tf": 1.7320508075688772}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1.7320508075688772}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1.7320508075688772}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1.7320508075688772}, "vertax.energy_shape_factor_homo": {"tf": 1}}, "df": 5}}}}, "t": {"docs": {}, "df": 0, "h": {"docs": {"vertax.Mesh.save_mesh": {"tf": 1.4142135623730951}, "vertax.Mesh.load_mesh": {"tf": 1.4142135623730951}, "vertax.PbcMesh.save_mesh": {"tf": 1.4142135623730951}, "vertax.PbcMesh.load_mesh": {"tf": 1.4142135623730951}, "vertax.PbcMesh.save_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.save_mesh": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.load_mesh": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1}}, "df": 8}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "n": {"docs": {"vertax.cost_checkerboard": {"tf": 1}}, "df": 1}}}}}}, "b": {"docs": {}, "df": 0, "c": {"docs": {"vertax.energy_line_tensions": {"tf": 1}}, "df": 1, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "h": {"docs": {"vertax": {"tf": 1.4142135623730951}, "vertax.Mesh": {"tf": 1}, "vertax.Mesh.__init__": {"tf": 1}, "vertax.PbcMesh": {"tf": 1}, "vertax.PbcMesh.save_mesh": {"tf": 1}, "vertax.PbcMesh.load_mesh": {"tf": 1}, "vertax.PbcBilevelOptimizer": {"tf": 1}, "vertax.cost_v2v": {"tf": 1}, "vertax.cost_mesh2image": {"tf": 1}, "vertax.cost_areas": {"tf": 1}}, "df": 10}}}}, "b": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "z": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"vertax": {"tf": 1}, "vertax.PbcMesh.update_boundary_conditions": {"tf": 1}}, "df": 2}}}}}}}}}}}}}}}}}}, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {"vertax": {"tf": 1}, "vertax.PbcMesh": {"tf": 1}, "vertax.PbcMesh.from_random_seeds": {"tf": 1}, "vertax.PbcMesh.from_seeds": {"tf": 1}, "vertax.PbcMesh.from_image": {"tf": 1.7320508075688772}, "vertax.PbcMesh.from_mask": {"tf": 1.4142135623730951}, "vertax.PbcMesh.update_boundary_conditions": {"tf": 1}, "vertax.PbcBilevelOptimizer": {"tf": 1}, "vertax.PbcBilevelOptimizer.__init__": {"tf": 1}}, "df": 9}}}}, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"vertax.PbcMesh.get_perimeter": {"tf": 1}, "vertax.FacePlot.PERIMETER": {"tf": 1}}, "df": 2}}}}}}, "f": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "m": {"docs": {"vertax.PbcMesh.from_image": {"tf": 1}}, "df": 1}}}}, "t": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"vertax.BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION": {"tf": 1}}, "df": 1}}, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"vertax.BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION": {"tf": 1}}, "df": 1}}}}}}}}}}, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "y": {"docs": {"vertax.energy_line_tensions": {"tf": 1}, "vertax.energy_line_tensions_bounded": {"tf": 1}}, "df": 2}}}}}}, "h": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "s": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}}, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "t": {"docs": {"vertax": {"tf": 1.4142135623730951}, "vertax.plot_mesh": {"tf": 1}, "vertax.get_plot_mesh": {"tf": 1}}, "df": 3}}}, "i": {"docs": {"vertax.BoundedMesh": {"tf": 1}, "vertax.BoundedMesh.angles": {"tf": 1}}, "df": 2}}, "w": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "h": {"docs": {"vertax": {"tf": 1}, "vertax.PbcMesh.from_image": {"tf": 1}, "vertax.PbcMesh.from_mask": {"tf": 1}, "vertax.BilevelOptimizationMethod": {"tf": 1}}, "df": 4}}, "t": {"docs": {}, "df": 0, "e": {"docs": {"vertax.FacePlot.WHITE": {"tf": 1}}, "df": 1}}, "l": {"docs": {}, "df": 0, "e": {"docs": {"vertax.cost_ratio": {"tf": 1}}, "df": 1}}}, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"vertax": {"tf": 1}, "vertax.Mesh.vertices": {"tf": 1}, "vertax.PbcMesh.save_mesh_txt": {"tf": 1}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1}, "vertax.energy_shape_factor_hetero": {"tf": 1}, "vertax.energy_shape_factor_homo": {"tf": 1}}, "df": 8}}, "n": {"docs": {"vertax.cost_checkerboard": {"tf": 1}}, "df": 1}}, "a": {"docs": {}, "df": 0, "t": {"docs": {"vertax.PbcMesh.create_empty": {"tf": 1}, "vertax.BoundedMesh.create_empty": {"tf": 1}, "vertax.FacePlot": {"tf": 1}, "vertax.EdgePlot": {"tf": 1}, "vertax.VertexPlot": {"tf": 1}}, "df": 5}}}, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"vertax": {"tf": 1.7320508075688772}, "vertax.PbcMesh": {"tf": 1.4142135623730951}, "vertax.PbcMesh.from_image": {"tf": 1.7320508075688772}, "vertax.PbcMesh.from_mask": {"tf": 1.7320508075688772}, "vertax.PbcMesh.get_length_with_offset": {"tf": 1}, "vertax.PbcMesh.update_boundary_conditions": {"tf": 1}, "vertax.BoundedMesh": {"tf": 2}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1}, "vertax.BilevelOptimizationMethod.AUTOMATIC_DIFFERENTIATION": {"tf": 1}, "vertax.PbcBilevelOptimizer.__init__": {"tf": 1}, "vertax.PbcBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.PbcBilevelOptimizer.compute_inner_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.__init__": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_inner_loss": {"tf": 1}, "vertax.cost_v2v_ias": {"tf": 1}, "vertax.cost_v2v_tem": {"tf": 1}, "vertax.cost_checkerboard": {"tf": 1.4142135623730951}}, "df": 18, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}, "d": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"vertax.Mesh.width": {"tf": 1}, "vertax.PbcMesh": {"tf": 1}, "vertax.PbcMesh.from_random_seeds": {"tf": 1.4142135623730951}, "vertax.PbcMesh.from_seeds": {"tf": 1.4142135623730951}, "vertax.BoundedMesh": {"tf": 1}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.from_seeds": {"tf": 1.7320508075688772}}, "df": 7, "/": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "t": {"docs": {"vertax.BoundedMesh.save_mesh_txt": {"tf": 1}}, "df": 1, "/": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "x": {"docs": {"vertax.PbcMesh.save_mesh_txt": {"tf": 1}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1}}, "df": 3}}}}}}}}}}}}}}, "l": {"docs": {}, "df": 0, "l": {"docs": {"vertax.PbcMesh.from_random_seeds": {"tf": 1.4142135623730951}, "vertax.PbcMesh.from_image": {"tf": 1.7320508075688772}, "vertax.PbcMesh.from_mask": {"tf": 1}, "vertax.energy_shape_factor_homo": {"tf": 1}}, "df": 4}}}, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "s": {"docs": {"vertax": {"tf": 1}}, "df": 1}}, "k": {"docs": {"vertax": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}}, "df": 2}}, "n": {"docs": {"vertax": {"tf": 1}}, "df": 1}}, "e": {"docs": {"vertax": {"tf": 1.7320508075688772}, "vertax.PbcMesh.from_image": {"tf": 1.4142135623730951}}, "df": 2, "i": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "t": {"docs": {"vertax.cost_v2v_ias": {"tf": 1}}, "df": 1, "s": {"docs": {"vertax.cost_v2v_tem": {"tf": 1}}, "df": 1}, "e": {"docs": {}, "df": 0, "d": {"docs": {"vertax.energy_line_tensions": {"tf": 1}, "vertax.energy_line_tensions_bounded": {"tf": 1}}, "df": 2}}}}}}}}, "s": {"docs": {"vertax.cost_IAS": {"tf": 1.4142135623730951}}, "df": 1, "p": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"vertax": {"tf": 1}}, "df": 1}}, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}}, "y": {"docs": {"vertax.PbcBilevelOptimizer.__init__": {"tf": 1}}, "df": 1}}, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "z": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"vertax": {"tf": 1}, "vertax.Mesh.__init__": {"tf": 1}}, "df": 2}}}}}}}}}}, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}}}}, "g": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}}}, "z": {"docs": {}, "df": 0, "e": {"docs": {"vertax.Mesh.width": {"tf": 1}, "vertax.Mesh.height": {"tf": 1}, "vertax.PbcMesh": {"tf": 1.7320508075688772}, "vertax.BoundedMesh": {"tf": 2}, "vertax.BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION": {"tf": 1}}, "df": 5}}}, "u": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "h": {"docs": {"vertax": {"tf": 1.4142135623730951}, "vertax.PbcMesh.__init__": {"tf": 1}}, "df": 2}}, "p": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}}}, "r": {"docs": {}, "df": 0, "e": {"docs": {"vertax": {"tf": 1}}, "df": 1}}, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"vertax.BoundedMesh": {"tf": 1}, "vertax.BoundedMesh.angles": {"tf": 1}}, "df": 2}}}}}}}}, "m": {"docs": {"vertax.cost_d_IAS": {"tf": 1}}, "df": 1}}, "h": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {"vertax": {"tf": 1}, "vertax.PbcMesh.from_seeds": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}, "vertax.energy_shape_factor_hetero": {"tf": 1}, "vertax.energy_shape_factor_homo": {"tf": 1}}, "df": 5}}}, "o": {"docs": {}, "df": 0, "w": {"docs": {"vertax.plot_mesh": {"tf": 1}, "vertax.FacePlot": {"tf": 1}, "vertax.EdgePlot": {"tf": 1}, "vertax.EdgePlot.INVISIBLE": {"tf": 1}, "vertax.VertexPlot": {"tf": 1}, "vertax.VertexPlot.INVISIBLE": {"tf": 1}}, "df": 6}}}, "o": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {"vertax.BilevelOptimizationMethod.IMPLICIT_DIFFERENTIATION": {"tf": 1}, "vertax.BilevelOptimizationMethod.ADJOINT_STATE": {"tf": 1}}, "df": 2, "s": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}, "m": {"docs": {}, "df": 0, "e": {"docs": {"vertax": {"tf": 1}}, "df": 1}}, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {"vertax.PbcMesh": {"tf": 1}, "vertax.BoundedMesh": {"tf": 1.4142135623730951}}, "df": 2}}}}}, "e": {"docs": {}, "df": 0, "t": {"docs": {"vertax": {"tf": 1}, "vertax.PbcMesh.from_random_seeds": {"tf": 1}, "vertax.BoundedBilevelOptimizer.__init__": {"tf": 1}}, "df": 3}, "e": {"docs": {"vertax": {"tf": 2}, "vertax.Mesh.__init__": {"tf": 1}}, "df": 2, "d": {"docs": {"vertax.PbcMesh.from_seeds": {"tf": 1}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1.4142135623730951}}, "df": 3, "s": {"docs": {"vertax.PbcMesh.__init__": {"tf": 1.4142135623730951}, "vertax.PbcMesh.from_random_seeds": {"tf": 2.23606797749979}, "vertax.PbcMesh.from_seeds": {"tf": 2.23606797749979}, "vertax.BoundedMesh.from_random_seeds": {"tf": 2.449489742783178}, "vertax.BoundedMesh.from_seeds": {"tf": 2.6457513110645907}}, "df": 5}}}, "l": {"docs": {}, "df": 0, "f": {"docs": {"vertax.PbcMesh.from_random_seeds": {"tf": 1}, "vertax.PbcMesh.from_image": {"tf": 1}, "vertax.PbcMesh.from_mask": {"tf": 1}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1}, "vertax.PbcBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.PbcBilevelOptimizer.compute_inner_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_inner_loss": {"tf": 1}}, "df": 10}}, "g": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"vertax.PbcMesh.from_image": {"tf": 1}}, "df": 1}}}}}}}}}}, "p": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"vertax.PbcMesh.save_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1}}, "df": 2}}}}}}, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {"vertax.BilevelOptimizationMethod.IMPLICIT_DIFFERENTIATION": {"tf": 1}, "vertax.BilevelOptimizationMethod.ADJOINT_STATE": {"tf": 1}}, "df": 2}}}}}}}}, "y": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "y": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}}}, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"vertax": {"tf": 1}, "vertax.cost_checkerboard": {"tf": 1}}, "df": 2}}, "v": {"docs": {}, "df": 0, "e": {"docs": {"vertax.Mesh.save_mesh": {"tf": 1}, "vertax.PbcMesh.save_mesh": {"tf": 1}, "vertax.PbcMesh.save_mesh_txt": {"tf": 1.7320508075688772}, "vertax.BoundedMesh.save_mesh": {"tf": 1}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1.7320508075688772}, "vertax.plot_mesh": {"tf": 1}}, "df": 6, "d": {"docs": {"vertax.Mesh.save_mesh": {"tf": 1.4142135623730951}, "vertax.PbcMesh.save_mesh": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.save_mesh": {"tf": 1.4142135623730951}}, "df": 3}}}}, "t": {"docs": {}, "df": 0, "r": {"docs": {"vertax.Mesh.save_mesh": {"tf": 1}, "vertax.Mesh.load_mesh": {"tf": 1}, "vertax.PbcMesh.save_mesh": {"tf": 1}, "vertax.PbcMesh.load_mesh": {"tf": 1}, "vertax.PbcMesh.save_mesh_txt": {"tf": 2.8284271247461903}, "vertax.PbcMesh.load_mesh_txt": {"tf": 2.8284271247461903}, "vertax.BoundedMesh.save_mesh": {"tf": 1}, "vertax.BoundedMesh.load_mesh": {"tf": 1}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 3}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 3}}, "df": 10, "u": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"vertax.Mesh": {"tf": 1}, "vertax.Mesh.__init__": {"tf": 1}}, "df": 2}, "a": {"docs": {}, "df": 0, "l": {"docs": {"vertax.cost_IAS": {"tf": 1}, "vertax.cost_d_IAS": {"tf": 1}}, "df": 2}}}}}}}}, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"vertax.PbcMesh.load_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1}}, "df": 2}}}}, "e": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "s": {"docs": {"vertax.BilevelOptimizationMethod.AUTOMATIC_DIFFERENTIATION": {"tf": 1}}, "df": 1}}}}, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"vertax.BilevelOptimizationMethod.AUTOMATIC_DIFFERENTIATION": {"tf": 1}}, "df": 1}}}}}, "q": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "{": {"docs": {}, "df": 0, "\\": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "m": {"docs": {"vertax.cost_IAS": {"tf": 1}}, "df": 1}}}}}}}}}, "n": {"docs": {}, "df": 0, "o": {"docs": {"vertax.PbcMesh.from_image": {"tf": 1}, "vertax.PbcMesh.from_mask": {"tf": 1.4142135623730951}, "vertax.BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION": {"tf": 1}}, "df": 3, "t": {"docs": {"vertax": {"tf": 1}, "vertax.Mesh": {"tf": 1}, "vertax.Mesh.__init__": {"tf": 1}, "vertax.PbcMesh.__init__": {"tf": 1}, "vertax.PbcMesh.save_mesh_txt": {"tf": 1}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.__init__": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1}, "vertax.PbcBilevelOptimizer.__init__": {"tf": 1}, "vertax.BoundedBilevelOptimizer.__init__": {"tf": 1}, "vertax.plot_mesh": {"tf": 1}, "vertax.EdgePlot.INVISIBLE": {"tf": 1}, "vertax.VertexPlot.INVISIBLE": {"tf": 1}}, "df": 15, "h": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"vertax.Mesh.__init__": {"tf": 1}}, "df": 1}}}}, "e": {"docs": {"vertax.BoundedMesh.from_seeds": {"tf": 1}}, "df": 1}}}, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}, "e": {"docs": {}, "df": 0, "d": {"docs": {"vertax": {"tf": 1}}, "df": 1, "e": {"docs": {}, "df": 0, "d": {"docs": {"vertax.BoundedMesh.from_random_seeds": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}}, "df": 2}}}}, "x": {"docs": {}, "df": 0, "t": {"docs": {"vertax.PbcMesh": {"tf": 1}, "vertax.BoundedMesh": {"tf": 1}}, "df": 2}}, "w": {"docs": {"vertax.PbcMesh.copy_mesh": {"tf": 1}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}, "vertax.BoundedMesh.copy_mesh": {"tf": 1}}, "df": 4}, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"vertax.PbcMesh.update_boundary_conditions": {"tf": 1}}, "df": 1}}}, "i": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"vertax.cost_checkerboard": {"tf": 1}}, "df": 1}}}}}}}}}}, "u": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"vertax.Mesh.nb_faces": {"tf": 1}, "vertax.Mesh.nb_edges": {"tf": 1}, "vertax.Mesh.nb_half_edges": {"tf": 1.4142135623730951}, "vertax.Mesh.nb_vertices": {"tf": 1}, "vertax.PbcMesh.MAX_EDGES_IN_ANY_FACE": {"tf": 1}, "vertax.PbcMesh.from_random_seeds": {"tf": 1}, "vertax.BoundedMesh.nb_angles": {"tf": 1}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1.7320508075688772}, "vertax.BoundedMesh.from_seeds": {"tf": 1.4142135623730951}, "vertax.BilevelOptimizationMethod.AUTOMATIC_DIFFERENTIATION": {"tf": 1}}, "df": 10}}}, "p": {"docs": {}, "df": 0, "y": {"docs": {"vertax.PbcMesh.save_mesh": {"tf": 1}, "vertax.PbcMesh.load_mesh": {"tf": 1.4142135623730951}, "vertax.PbcMesh.save_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1}}, "df": 4}}}, "d": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"vertax.BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION": {"tf": 1}}, "df": 1}, "s": {"docs": {"vertax.cost_ratio": {"tf": 1}, "vertax.cost_checkerboard": {"tf": 1}}, "df": 2}}}}}, "p": {"docs": {}, "df": 0, "z": {"docs": {"vertax.Mesh.save_mesh": {"tf": 1}, "vertax.Mesh.load_mesh": {"tf": 1.4142135623730951}, "vertax.PbcMesh.save_mesh": {"tf": 1}, "vertax.PbcMesh.load_mesh": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.save_mesh": {"tf": 1}, "vertax.BoundedMesh.load_mesh": {"tf": 1.4142135623730951}}, "df": 6}}, "b": {"docs": {"vertax.PbcMesh": {"tf": 1.7320508075688772}, "vertax.PbcMesh.from_random_seeds": {"tf": 1}, "vertax.BoundedMesh": {"tf": 2}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.from_seeds": {"tf": 1}}, "df": 5, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "s": {"docs": {"vertax.PbcMesh.from_seeds": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}}, "df": 2}}}}}}, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "y": {"docs": {"vertax.PbcMesh.from_image": {"tf": 1}, "vertax.PbcMesh.from_mask": {"tf": 1}}, "df": 2}}}}}}, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"vertax.cost_ratio": {"tf": 1}}, "df": 1}}}}}}}}}, "h": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {"vertax": {"tf": 1.4142135623730951}, "vertax.PbcMesh.from_seeds": {"tf": 1}, "vertax.PbcMesh.update_boundary_conditions": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}}, "df": 4}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"vertax.cost_checkerboard": {"tf": 1}}, "df": 1}}}}, "l": {"docs": {}, "df": 0, "f": {"docs": {"vertax.Mesh.edges": {"tf": 1}, "vertax.Mesh.nb_half_edges": {"tf": 1}, "vertax.PbcMesh": {"tf": 2.449489742783178}, "vertax.PbcMesh.save_mesh_txt": {"tf": 1.4142135623730951}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1.4142135623730951}, "vertax.BoundedMesh": {"tf": 3.1622776601683795}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1.4142135623730951}, "vertax.cost_tem_halfedge": {"tf": 1}}, "df": 9, "e": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {"vertax.cost_v2v_tem": {"tf": 1}}, "df": 1}}}}}}}, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"vertax": {"tf": 1}}, "df": 1}}, "i": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "t": {"docs": {"vertax.Mesh.height": {"tf": 1}, "vertax.PbcMesh": {"tf": 1}, "vertax.PbcMesh.from_random_seeds": {"tf": 1.4142135623730951}, "vertax.PbcMesh.from_seeds": {"tf": 1.4142135623730951}, "vertax.BoundedMesh": {"tf": 1}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.from_seeds": {"tf": 1.7320508075688772}}, "df": 7}}}}, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"vertax.PbcMesh.save_mesh_txt": {"tf": 1}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1}}, "df": 4}}}}}, "p": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"vertax.PbcMesh.save_mesh_txt": {"tf": 1}, "vertax.PbcMesh.load_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.save_mesh_txt": {"tf": 1}, "vertax.BoundedMesh.load_mesh_txt": {"tf": 1}}, "df": 4}}}}}}}}}}}, "s": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {"vertax.BilevelOptimizationMethod.IMPLICIT_DIFFERENTIATION": {"tf": 1}, "vertax.BilevelOptimizationMethod.ADJOINT_STATE": {"tf": 1}}, "df": 2}}}}}}, "y": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}, "o": {"docs": {}, "df": 0, "w": {"docs": {"vertax": {"tf": 1}}, "df": 1, "e": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"vertax": {"tf": 1}}, "df": 1}}}}}, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"vertax.PbcMesh.from_image": {"tf": 1.4142135623730951}, "vertax.PbcMesh.from_mask": {"tf": 1.4142135623730951}}, "df": 2}}}}}, "q": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "d": {"docs": {"vertax": {"tf": 1.4142135623730951}}, "df": 1}}}}, "x": {"docs": {"vertax": {"tf": 1.7320508075688772}, "vertax.Mesh.width": {"tf": 1}, "vertax.PbcMesh": {"tf": 1.4142135623730951}, "vertax.PbcMesh.from_seeds": {"tf": 1}, "vertax.PbcMesh.get_length_with_offset": {"tf": 1}, "vertax.BoundedMesh": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}}, "df": 7, "^": {"docs": {}, "df": 0, "{": {"docs": {}, "df": 0, "\\": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {"vertax": {"tf": 1.4142135623730951}}, "df": 1}}}}}}}, "g": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {"vertax.energy_shape_factor_homo": {"tf": 1}}, "df": 1, "n": {"docs": {"vertax": {"tf": 1}, "vertax.PbcMesh.get_length": {"tf": 1}, "vertax.PbcMesh.get_length_with_offset": {"tf": 1}, "vertax.PbcMesh.get_perimeter": {"tf": 1}, "vertax.PbcMesh.get_area": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}, "vertax.cost_v2v": {"tf": 1}, "vertax.cost_mesh2image": {"tf": 1}, "vertax.cost_areas": {"tf": 1}}, "df": 9}}}}, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {"vertax.Mesh": {"tf": 1}}, "df": 1}}, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {"vertax.BoundedMesh.from_random_seeds": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}}, "df": 2}}}}}}}, "t": {"docs": {"vertax.Mesh.nb_faces": {"tf": 1}, "vertax.Mesh.nb_edges": {"tf": 1}, "vertax.Mesh.nb_half_edges": {"tf": 1}, "vertax.Mesh.nb_vertices": {"tf": 1}, "vertax.PbcMesh.get_length": {"tf": 1}, "vertax.PbcMesh.get_length_with_offset": {"tf": 1}, "vertax.PbcMesh.get_perimeter": {"tf": 1}, "vertax.PbcMesh.get_area": {"tf": 1}, "vertax.BoundedMesh.nb_angles": {"tf": 1}, "vertax.BoundedMesh.get_length": {"tf": 1}, "vertax.BoundedMesh.get_perimeter": {"tf": 1}, "vertax.BoundedMesh.get_area": {"tf": 1}, "vertax.PbcBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.PbcBilevelOptimizer.compute_inner_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_outer_loss": {"tf": 1}, "vertax.BoundedBilevelOptimizer.compute_inner_loss": {"tf": 1}, "vertax.get_plot_mesh": {"tf": 1}, "vertax.FacePlot.MULTICOLOR": {"tf": 1}}, "df": 18}}, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"vertax.BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION": {"tf": 1}}, "df": 1}}}}}, "p": {"docs": {}, "df": 0, "h": {"docs": {"vertax.cost_tem_halfedge": {"tf": 1}}, "df": 1}}}}}, "y": {"docs": {"vertax.Mesh.height": {"tf": 1}, "vertax.PbcMesh": {"tf": 1}, "vertax.PbcMesh.from_seeds": {"tf": 1}, "vertax.PbcMesh.get_length_with_offset": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}}, "df": 5, "o": {"docs": {}, "df": 0, "u": {"docs": {"vertax": {"tf": 1.7320508075688772}, "vertax.PbcMesh.from_image": {"tf": 1}, "vertax.PbcMesh.create_empty": {"tf": 1.4142135623730951}, "vertax.PbcMesh.update_boundary_conditions": {"tf": 1}, "vertax.BoundedMesh.create_empty": {"tf": 1.4142135623730951}}, "df": 5, "r": {"docs": {"vertax": {"tf": 1.7320508075688772}, "vertax.BoundedMesh.from_seeds": {"tf": 1}}, "df": 2, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "f": {"docs": {"vertax.PbcMesh.update_boundary_conditions": {"tf": 1}}, "df": 1}}}}}}}, "e": {"docs": {}, "df": 0, "t": {"docs": {"vertax.PbcBilevelOptimizer.__init__": {"tf": 1}, "vertax.BoundedBilevelOptimizer.__init__": {"tf": 1}}, "df": 2}}}, "k": {"docs": {"vertax.cost_IAS": {"tf": 1.7320508075688772}, "vertax.cost_d_IAS": {"tf": 1.7320508075688772}}, "df": 2, "e": {"docs": {}, "df": 0, "y": {"docs": {"vertax.PbcMesh.from_random_seeds": {"tf": 1.4142135623730951}, "vertax.BoundedMesh.from_random_seeds": {"tf": 1}, "vertax.BoundedMesh.from_seeds": {"tf": 1}}, "df": 3}}, "n": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "w": {"docs": {"vertax.PbcMesh.create_empty": {"tf": 1}, "vertax.BoundedMesh.create_empty": {"tf": 1}}, "df": 2}}}}}}}, "pipeline": ["trimmer"], "_isPrebuiltIndex": true};
+
+ // mirrored in build-search-index.js (part 1)
+ // Also split on html tags. this is a cheap heuristic, but good enough.
+ elasticlunr.tokenizer.setSeperator(/[\s\-.;&_'"=,()]+|<[^>]*>/);
+
+ let searchIndex;
+ if (docs._isPrebuiltIndex) {
+ console.info("using precompiled search index");
+ searchIndex = elasticlunr.Index.load(docs);
+ } else {
+ console.time("building search index");
+ // mirrored in build-search-index.js (part 2)
+ searchIndex = elasticlunr(function () {
+ this.pipeline.remove(elasticlunr.stemmer);
+ this.pipeline.remove(elasticlunr.stopWordFilter);
+ this.addField("qualname");
+ this.addField("fullname");
+ this.addField("annotation");
+ this.addField("default_value");
+ this.addField("signature");
+ this.addField("bases");
+ this.addField("doc");
+ this.setRef("fullname");
+ });
+ for (let doc of docs) {
+ searchIndex.addDoc(doc);
+ }
+ console.timeEnd("building search index");
+ }
+
+ return (term) => searchIndex.search(term, {
+ fields: {
+ qualname: {boost: 4},
+ fullname: {boost: 2},
+ annotation: {boost: 2},
+ default_value: {boost: 2},
+ signature: {boost: 2},
+ bases: {boost: 2},
+ doc: {boost: 1},
+ },
+ expand: true
+ });
+})();
\ No newline at end of file
diff --git a/docs/vertax.html b/docs/vertax.html
new file mode 100644
index 0000000..1a3b3bf
--- /dev/null
+++ b/docs/vertax.html
@@ -0,0 +1,7436 @@
+
+
+
+
+
+
+ VertAX API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+VertAX
+
+ A differentiable JAX-based framework for vertex modeling and inverse design of epithelial tissues.
+
+
Epithelial tissues dynamically reshape through local mechanical interactions among cells.
+Understanding, inferring, and designing these mechanics is a central challenge in developmental biology and biophysics.
+VertAX is a computational framework built to address this challenge.
+
+
VertAX is a framework for vertex-based modeling :
+ it represents epithelial tissues as two-dimensional polygonal meshes in which cells are faces, junctions are edges,
+ tricellular contacts are vertices, and mechanical equilibrium is defined by the minimum of a user-specified energy.
+
+
Built on JAX , VertAX is designed not only for forward simulation,
+ but also for inverse problems such as parameter inference and tissue design.
+
+
In VertAX , a Mesh represents a vertex model of an epithelial tissue. It is made of faces (cells), edges (interface between cells) and vertices (where 3 cells or more meet).
+
+
More specifically, two types of meshes are currently supported:
+
+
+PbcMesh have Periodic Boundary Conditions, and are used for bulk tissue dynamics without explicit external boundaries.
+BoundedMesh are designed for finite tissue clusters, with curved free interfaces.
+
+
+
The other first class objects of VertAX are called Bilevel Optimizers. They allow to formulate inverse problems as nested optimizations:
+
+
$$
+\begin{aligned}
+\textbf{Outer problem (learning):} \quad
+\theta^{\ast} &= \arg\min_{\theta} \mathcal{C}\left(X^{\ast}_{\theta},\theta\right)
+&& \leftarrow \text{fit data or reach a target} \
+\end{aligned}
+$$
+$$
+\begin{aligned}
+\textbf{Inner problem (physics):} \ \ \text{s.t.}\ \ &
+X^{\ast}_{\theta} \in \arg\min_{X} \mathcal{E}(X,\theta)
+&& \leftarrow \text{compute mechanical equilibrium}
+\end{aligned}
+$$
+
+
Here, $X$ denotes the tissue configuration, i.e. the vertex positions of the mesh, and $\theta$ denotes the model parameters, such as line tensions, target areas, or shape factors.
+
+
In other words, VertAX repeatedly solves a mechanical equilibrium problem for a given parameter set $\theta$, then updates those parameters to better match data or a design objective.
+
+
In symmetry with meshes, a base abstract class _BilevelOptimizer defines common hyper-parameters and methods for the bilevel optimization, but you need to use the specialized classes:
+
+
+
+
VertAX comes with pre-defined energy and cost functions but you can easily define your own functions. See the examples folder in the repository to have a typical example on how to use VertAX .
+
+
Finally, there are plot functions to easily see the results of your experiments. See plot_mesh for example.
+
+
Users can define their own energy functions for the inner optimization and cost function for the outer optimization, however we provide some basic ones.
+If you use your own make sure to use the exact same signature as we do for these functions, otherwise it won't work. See the cost and energy functions we provide in this documentation.
+
+
+
+
+ View Source
+
+ 1 """A differentiable JAX-based framework for vertex modeling and inverse design of epithelial tissues.
+ 2
+ 3 Epithelial tissues dynamically reshape through local mechanical interactions among cells.
+ 4 Understanding, inferring, and designing these mechanics is a central challenge in developmental biology and biophysics.
+ 5 **VertAX** is a computational framework built to address this challenge.
+ 6
+ 7 **VertAX** is a **framework for vertex-based modeling**:
+ 8 it represents epithelial tissues as two-dimensional polygonal meshes in which cells are faces, junctions are edges,
+ 9 tricellular contacts are vertices, and mechanical equilibrium is defined by the minimum of a user-specified energy.
+ 10
+ 11 Built on **JAX**, VertAX is designed not only for forward simulation,
+ 12 but also for inverse problems such as parameter inference and tissue design.
+ 13
+ 14 In **VertAX**, a `Mesh` represents a vertex model of an epithelial tissue. It is made of faces (cells), edges (interface between cells) and vertices (where 3 cells or more meet).
+ 15
+ 16 More specifically, two types of meshes are currently supported:
+ 17 - `PbcMesh` have Periodic Boundary Conditions, and are used for bulk tissue dynamics without explicit external boundaries.
+ 18 - `BoundedMesh` are designed for finite tissue clusters, with curved free interfaces.
+ 19
+ 20 The other first class objects of **VertAX** are called Bilevel Optimizers. They allow to formulate inverse problems as nested optimizations:
+ 21
+ 22 $$
+ 23 \\begin{aligned}
+ 24 \\textbf{Outer problem (learning):} \\quad
+ 25 \\theta^{\\ast} &= \\arg\\min_{\\theta} \\mathcal{C}\\left(X^{\\ast}_{\\theta},\\theta\\right)
+ 26 && \\leftarrow \\text{fit data or reach a target} \\\\
+ 27 \\end{aligned}
+ 28 $$
+ 29 $$
+ 30 \\begin{aligned}
+ 31 \\textbf{Inner problem (physics):} \\quad \\text{s.t.}&
+ 32 X^{\\ast}_{\\theta} \\in \\arg\\min_{X} \\mathcal{E}(X,\\theta)
+ 33 && \\leftarrow \\text{compute mechanical equilibrium}
+ 34 \\end{aligned}
+ 35 $$
+ 36
+ 37
+ 38 Here, $X$ denotes the tissue configuration, i.e. the vertex positions of the mesh, and $\\theta$ denotes the model parameters, such as line tensions, target areas, or shape factors.
+ 39
+ 40 In other words, VertAX repeatedly solves a mechanical equilibrium problem for a given parameter set $\\theta$, then updates those parameters to better match data or a design objective.
+ 41
+ 42 In symmetry with meshes, a base abstract class `_BilevelOptimizer` defines common hyper-parameters and methods for the bilevel optimization, but you need to use the specialized classes:
+ 43 - `PbcBilevelOptimizer` for `PbcMesh`,
+ 44 - `BoundedBilevelOptimizer` for `BoundedMesh`.
+ 45
+ 46 **VertAX** comes with pre-defined energy and cost functions but you can easily define your own functions. See the `examples` folder in the repository to have a typical example on how to use **VertAX**.
+ 47
+ 48 Finally, there are plot functions to easily see the results of your experiments. See `plot_mesh` for example.
+ 49
+ 50 Users can define their own energy functions for the inner optimization and cost function for the outer optimization, however we provide some basic ones.
+ 51 If you use your own make sure to use the exact same signature as we do for these functions, otherwise it won't work. See the cost and energy functions we provide in this documentation.
+ 52 """ # noqa: D301, E501
+ 53
+ 54 from vertax.bilevelopt.bilevelopt import _BilevelOptimizer
+ 55 from vertax.bilevelopt.boundedbop import BoundedBilevelOptimizer
+ 56 from vertax.bilevelopt.pbcbop import PbcBilevelOptimizer
+ 57 from vertax.cost import (
+ 58 cost_areas ,
+ 59 cost_checkerboard ,
+ 60 cost_d_IAS ,
+ 61 cost_IAS ,
+ 62 cost_mesh2image ,
+ 63 cost_ratio ,
+ 64 cost_tem_halfedge ,
+ 65 cost_v2v ,
+ 66 cost_v2v_ias ,
+ 67 cost_v2v_tem ,
+ 68 )
+ 69 from vertax.energy import (
+ 70 energy_line_tensions ,
+ 71 energy_line_tensions_bounded ,
+ 72 energy_shape_factor_hetero ,
+ 73 energy_shape_factor_homo ,
+ 74 )
+ 75 from vertax.meshes.bounded_mesh import BoundedMesh
+ 76 from vertax.meshes.mesh import Mesh
+ 77 from vertax.meshes.pbc_mesh import PbcMesh
+ 78 from vertax.meshes.plot import EdgePlot , FacePlot , VertexPlot , get_plot_mesh , plot_mesh
+ 79 from vertax.method_enum import BilevelOptimizationMethod
+ 80
+ 81 __all__ = [ # noqa: RUF022
+ 82 "Mesh" ,
+ 83 "PbcMesh" ,
+ 84 "BoundedMesh" ,
+ 85 "BilevelOptimizationMethod" ,
+ 86 "_BilevelOptimizer" ,
+ 87 "PbcBilevelOptimizer" ,
+ 88 "BoundedBilevelOptimizer" ,
+ 89 "plot_mesh" ,
+ 90 "get_plot_mesh" ,
+ 91 "FacePlot" ,
+ 92 "EdgePlot" ,
+ 93 "VertexPlot" ,
+ 94 "cost_v2v" ,
+ 95 "cost_mesh2image" ,
+ 96 "cost_areas" ,
+ 97 "cost_IAS" ,
+ 98 "cost_d_IAS" ,
+ 99 "cost_tem_halfedge" ,
+100 "cost_v2v_ias" ,
+101 "cost_v2v_tem" ,
+102 "cost_ratio" ,
+103 "cost_checkerboard" ,
+104 "energy_shape_factor_hetero" ,
+105 "energy_shape_factor_homo" ,
+106 "energy_line_tensions" ,
+107 "energy_line_tensions_bounded" ,
+108 ]
+
+
+
+
+
+
+
+
+ class
+ Mesh :
+
+ View Source
+
+
+
+ 37 class Mesh ( metaclass = NoPublicConstructor ):
+ 38 """Generic mesh structure. It is an abstract base class, not to be used directly.
+ 39
+ 40 It defines common attributes and functions between `PbcMesh` and `BoundedMesh`.
+ 41 """
+ 42
+ 43 def __init__ ( self ) -> None :
+ 44 """Do nothing but create attributes. Do not call this, but call specialized class methods to create meshes.
+ 45
+ 46 See `PbcMesh` and `BoundedMesh`.
+ 47
+ 48 Technically, it uses a DCEL structure.
+ 49 """
+ 50 self . faces : Array = jnp . array ([])
+ 51 """The cells of the tissue."""
+ 52 self . edges : Array = jnp . array ([])
+ 53 """The interface between cells. Technically half-edges."""
+ 54 self . vertices : Array = jnp . array ([])
+ 55 """The mesh vertices, where cells meet."""
+ 56 self . width : float = 0
+ 57 """The mesh live in a rectangle of size [0, width] in the X direction."""
+ 58 self . height : float = 0
+ 59 """The mesh live in a rectangle of size [0, height] in the Y direction."""
+ 60
+ 61 self . faces_params : Array = jnp . array ([])
+ 62 """Parameters attached to faces. Can be optimized."""
+ 63 self . edges_params : Array = jnp . array ([])
+ 64 """Parameters attached to edges. Can be optimized."""
+ 65 self . vertices_params : Array = jnp . array ([])
+ 66 """Parameters attached to vertices. Can be optimized."""
+ 67
+ 68 @property
+ 69 def nb_faces ( self ) -> int :
+ 70 """Get the number of faces of the mesh."""
+ 71 return len ( self . faces )
+ 72
+ 73 @property
+ 74 def nb_edges ( self ) -> int :
+ 75 """Get the number of edges of the mesh."""
+ 76 return self . nb_half_edges // 2
+ 77
+ 78 @property
+ 79 def nb_half_edges ( self ) -> int :
+ 80 """Get the number of half-edges of the mesh, ie. twice the number of edges."""
+ 81 return len ( self . edges )
+ 82
+ 83 @property
+ 84 def nb_vertices ( self ) -> int :
+ 85 """Get the number of vertices of the mesh."""
+ 86 return len ( self . vertices )
+ 87
+ 88 def save_mesh ( self , path : str ) -> None :
+ 89 """Save mesh to a file.
+ 90
+ 91 All mesh data is saved.
+ 92
+ 93 Args:
+ 94 path (str): Path to the saved file. The extension is .npz.
+ 95 """
+ 96 raise NotImplementedError
+ 97
+ 98 @classmethod
+ 99 def load_mesh ( cls , path : str ) -> Self :
+100 """Load a mesh from a file.
+101
+102 Args:
+103 path (str): Path to the mesh file (.npz).
+104
+105 Returns:
+106 The mesh loaded from the .npz file.
+107 """
+108 raise NotImplementedError
+
+
+
+ Generic mesh structure. It is an abstract base class, not to be used directly.
+
+
It defines common attributes and functions between PbcMesh and BoundedMesh .
+
+
+
+
+
+
+
+ Mesh ()
+
+ View Source
+
+
+
+
43 def __init__ ( self ) -> None :
+44 """Do nothing but create attributes. Do not call this, but call specialized class methods to create meshes.
+45
+46 See `PbcMesh` and `BoundedMesh`.
+47
+48 Technically, it uses a DCEL structure.
+49 """
+50 self . faces : Array = jnp . array ([])
+51 """The cells of the tissue."""
+52 self . edges : Array = jnp . array ([])
+53 """The interface between cells. Technically half-edges."""
+54 self . vertices : Array = jnp . array ([])
+55 """The mesh vertices, where cells meet."""
+56 self . width : float = 0
+57 """The mesh live in a rectangle of size [0, width] in the X direction."""
+58 self . height : float = 0
+59 """The mesh live in a rectangle of size [0, height] in the Y direction."""
+60
+61 self . faces_params : Array = jnp . array ([])
+62 """Parameters attached to faces. Can be optimized."""
+63 self . edges_params : Array = jnp . array ([])
+64 """Parameters attached to edges. Can be optimized."""
+65 self . vertices_params : Array = jnp . array ([])
+66 """Parameters attached to vertices. Can be optimized."""
+
+
+
+
Do nothing but create attributes. Do not call this, but call specialized class methods to create meshes.
+
+
See PbcMesh and BoundedMesh .
+
+
Technically, it uses a DCEL structure.
+
+
+
+
+
+
+ faces : jax.Array
+
+
+
+
+
+
The cells of the tissue.
+
+
+
+
+
+
+ edges : jax.Array
+
+
+
+
+
+
The interface between cells. Technically half-edges.
+
+
+
+
+
+
+ vertices : jax.Array
+
+
+
+
+
+
The mesh vertices, where cells meet.
+
+
+
+
+
+
+ width : float
+
+
+
+
+
+
The mesh live in a rectangle of size [0, width] in the X direction.
+
+
+
+
+
+
+ height : float
+
+
+
+
+
+
The mesh live in a rectangle of size [0, height] in the Y direction.
+
+
+
+
+
+
+ faces_params : jax.Array
+
+
+
+
+
+
Parameters attached to faces. Can be optimized.
+
+
+
+
+
+
+ edges_params : jax.Array
+
+
+
+
+
+
Parameters attached to edges. Can be optimized.
+
+
+
+
+
+
+ vertices_params : jax.Array
+
+
+
+
+
+
Parameters attached to vertices. Can be optimized.
+
+
+
+
+
+
+
+ nb_faces : int
+
+ View Source
+
+
+
+
68 @property
+69 def nb_faces ( self ) -> int :
+70 """Get the number of faces of the mesh."""
+71 return len ( self . faces )
+
+
+
+
Get the number of faces of the mesh.
+
+
+
+
+
+
+
+ nb_edges : int
+
+ View Source
+
+
+
+
73 @property
+74 def nb_edges ( self ) -> int :
+75 """Get the number of edges of the mesh."""
+76 return self . nb_half_edges // 2
+
+
+
+
Get the number of edges of the mesh.
+
+
+
+
+
+
+
+ nb_half_edges : int
+
+ View Source
+
+
+
+
78 @property
+79 def nb_half_edges ( self ) -> int :
+80 """Get the number of half-edges of the mesh, ie. twice the number of edges."""
+81 return len ( self . edges )
+
+
+
+
Get the number of half-edges of the mesh, ie. twice the number of edges.
+
+
+
+
+
+
+
+ nb_vertices : int
+
+ View Source
+
+
+
+
83 @property
+84 def nb_vertices ( self ) -> int :
+85 """Get the number of vertices of the mesh."""
+86 return len ( self . vertices )
+
+
+
+
Get the number of vertices of the mesh.
+
+
+
+
+
+
+
+
+ def
+ save_mesh (self , path : str ) -> None :
+
+ View Source
+
+
+
+
88 def save_mesh ( self , path : str ) -> None :
+89 """Save mesh to a file.
+90
+91 All mesh data is saved.
+92
+93 Args:
+94 path (str): Path to the saved file. The extension is .npz.
+95 """
+96 raise NotImplementedError
+
+
+
+
Save mesh to a file.
+
+
All mesh data is saved.
+
+
Arguments:
+
+
+path (str): Path to the saved file. The extension is .npz.
+
+
+
+
+
+
+
+
+
@classmethod
+
+
def
+
load_mesh (cls , path : str ) -> Self :
+
+
View Source
+
+
+
+
98 @classmethod
+ 99 def load_mesh ( cls , path : str ) -> Self :
+100 """Load a mesh from a file.
+101
+102 Args:
+103 path (str): Path to the mesh file (.npz).
+104
+105 Returns:
+106 The mesh loaded from the .npz file.
+107 """
+108 raise NotImplementedError
+
+
+
+
Load a mesh from a file.
+
+
Arguments:
+
+
+path (str): Path to the mesh file (.npz).
+
+
+
Returns:
+
+
+ The mesh loaded from the .npz file.
+
+
+
+
+
+
+
+
+
+
+ 21 class PbcMesh ( Mesh ):
+ 22 """Periodic Boundary Condition on a mesh.
+ 23
+ 24 For a PbcMesh, `vertices` is a 2D array of floats of size (nb_vertices, 2) ;
+ 25 with the coordinates of the vertices (in ]0, width[ x ]0, height[ ).
+ 26
+ 27 `edges` is a 2D array of integers of size (nb_half_edges, 8), with:
+ 28
+ 29 - id of previous half-edge,
+ 30 - id of next half-edge,
+ 31 - id of twin half-edge,
+ 32 - id of source vertex,
+ 33 - id of target vertex,
+ 34 - id of the face containing the half-edge.
+ 35 - x-offset (either -1, 0 or 1) depending on the target vertex crossing a boundary.
+ 36 - y-offset (either -1, 0 or 1) depending on the target vertex crossing a boundary.
+ 37
+ 38 `faces` is a 1D array of integers of size (nb_faces) containing the id of a half-edge belonging to this face.
+ 39 """
+ 40
+ 41 def __init__ ( self ) -> None :
+ 42 """Do not call the constructor directly, use the dedicated class methods such as:
+ 43
+ 44 - `from_random_seeds`,
+ 45 - `from_seeds`,
+ 46 - `from_image`,
+ 47 - `from_mask`,
+ 48 - `create_empty`.
+ 49 """ # noqa: D415
+ 50 super () . __init__ ()
+ 51
+ 52 self . MAX_EDGES_IN_ANY_FACE : int = 20
+ 53 """Optimization parameter : must be more than the estimated maximum number of edges in a face. Base value is 20.""" # noqa: E501
+ 54
+ 55 @classmethod
+ 56 def from_random_seeds ( cls , nb_seeds : int , width : float , height : float , random_key : int ) -> Self :
+ 57 """Create a Periodic Boundary Conditions Mesh from a Voronoi diagram based on random seeds.
+ 58
+ 59 Args:
+ 60 nb_seeds (int): Number of random seeds to use.
+ 61 width (float): Width of the rectangular domains the seeds will be in.
+ 62 height (float): Height of the rectangular domains the seeds will be in.
+ 63 random_key (int): Set the random key for reproducibility.
+ 64
+ 65 Returns:
+ 66 Self: The corresponding mesh.
+ 67 """
+ 68 key = jax . random . PRNGKey ( random_key )
+ 69 seeds = jnp . array (( width , height )) * jax . random . uniform ( key , ( nb_seeds , 2 ))
+ 70 return cls . from_seeds ( seeds , width , height )
+ 71
+ 72 @classmethod
+ 73 def from_seeds ( cls , seeds : Array , width : float , height : float ) -> Self :
+ 74 """Create a Periodic Boundary Conditions Mesh from a Voronoi diagram based on a list of seeds.
+ 75
+ 76 The seeds are assumed to have positive x and y positions.
+ 77
+ 78 Args:
+ 79 seeds (Array[float32]): jax float array of seed positions of shape (nbSeeds, 2).
+ 80 width (float): width of the box containing the seeds.
+ 81 height (float): height of the box containing the seeds.
+ 82 """
+ 83 (
+ 84 periodic_voronoi_vertices_idx ,
+ 85 periodic_voronoi_vertices_pos ,
+ 86 periodic_voronoi_edges ,
+ 87 offsets ,
+ 88 periodic_voronoi_faces ,
+ 89 ) = _make_periodic ( seeds , width , height )
+ 90
+ 91 vertices , edges , faces = _make_he_structure (
+ 92 width ,
+ 93 height ,
+ 94 periodic_voronoi_vertices_idx ,
+ 95 periodic_voronoi_vertices_pos ,
+ 96 periodic_voronoi_edges ,
+ 97 offsets ,
+ 98 periodic_voronoi_faces ,
+ 99 )
+100
+101 pbc_mesh = cls . _create ()
+102 pbc_mesh . vertices = jnp . array ( vertices , dtype = np . float32 )
+103 pbc_mesh . edges = jnp . array ( edges , dtype = np . int32 )
+104 pbc_mesh . faces = jnp . array ( faces , dtype = np . int32 )
+105 pbc_mesh . width = width
+106 pbc_mesh . height = height
+107
+108 return pbc_mesh
+109
+110 @classmethod
+111 def from_image (
+112 cls ,
+113 image : NDArray ,
+114 ) -> Self :
+115 """Create a rudimentary mesh with periodic boundary conditions from an image.
+116
+117 To do that, we perform a segmentation using Cellpose and we try to fill the holes.
+118 The result will probably be imperfect and it will always be better if you
+119 provide directly a mask (with no holes) with the function "periodic_from_mask".
+120
+121 The mask is made periodic by mirroring its edges.
+122
+123 Args:
+124 image (NDArray): The image which will act as a template for the mesh.
+125
+126 Returns:
+127 Self: The corresponding mesh.
+128 """
+129 return cls . from_mask ( mask_from_image ( image ))
+130
+131 @classmethod
+132 def from_mask ( # noqa: C901
+133 cls ,
+134 mask : NDArray ,
+135 ) -> Self :
+136 """Create a rudimentary mesh with periodic boundary conditions from a mask with no holes.
+137
+138 The mask is made periodic by mirroring its edges.
+139
+140 Args:
+141 mask (NDArray): The mask with no holes which will act as a template for the mesh.
+142
+143 Returns:
+144 Self: The corresponding mesh.
+145 """
+146 padded_mask = pad ( mask , save = False , output_path = "refined_and_padded_image.tiff" )
+147 # Find vertices, edges, faces
+148 vertices , edges , faces = find_vertices_edges_faces ( padded_mask )
+149
+150 # imread tiff = Y is the first axis, X the second.
+151 height : int = mask . shape [ 0 ] # original image length. Padded is 3 times bigger.
+152 y_min = height / 2
+153 y_max = 2 * height + ( height / 2 )
+154 width : int = mask . shape [ 1 ]
+155 x_min = width / 2
+156 x_max = 2 * width + ( width / 2 )
+157
+158 col0_mask = ( vertices [:, 0 ] >= x_min ) & ( vertices [:, 0 ] < x_max )
+159 col1_mask = ( vertices [:, 1 ] >= y_min ) & ( vertices [:, 1 ] < y_max )
+160
+161 periodic_vertices_idx = np . arange ( len ( vertices ))[ col0_mask & col1_mask ]
+162 periodic_vertices_pos = vertices [ col0_mask & col1_mask ]
+163
+164 # store map between vertex id -> inside vertex id
+165 inside_vertex : dict [ int , int ] = { idx : idx for idx in periodic_vertices_idx }
+166 for i , vertex in enumerate ( vertices ):
+167 if i not in periodic_vertices_idx :
+168 x , y = vertex
+169 if x < x_min :
+170 x += 2 * width
+171 elif x >= x_max :
+172 x -= 2 * width
+173
+174 if y < y_min :
+175 y += 2 * height
+176 elif y >= y_max :
+177 y -= 2 * height
+178
+179 # Find corresponding inside vertex to the outside dest vertex
+180 for idx , pos in zip ( periodic_vertices_idx , periodic_vertices_pos , strict = True ):
+181 if np . max ( np . abs ( pos - [ x , y ])) < 1 :
+182 inside_vertex [ i ] = idx
+183 break
+184
+185 edges_inside = []
+186 edges_outside = []
+187 offsets_inside = {}
+188 offsets_outside = {}
+189 visited = []
+190
+191 for e in edges :
+192 if e [ 0 ] in periodic_vertices_idx and e [ 1 ] in periodic_vertices_idx :
+193 edges_inside . append ( tuple ( sorted (( e [ 0 ], e [ 1 ]))))
+194 offsets_inside [( e [ 0 ], e [ 1 ])] = ( 0 , 0 )
+195 offsets_inside [( e [ 1 ], e [ 0 ])] = ( 0 , 0 )
+196 elif bool ( e [ 0 ] in periodic_vertices_idx ) != bool ( e [ 1 ] in periodic_vertices_idx ):
+197 if e [ 0 ] in periodic_vertices_idx :
+198 # origin in, dest out
+199 # check x coord
+200 if vertices [ e [ 1 ]][ 0 ] < x_min :
+201 offset_x1 = - 1
+202 elif vertices [ e [ 1 ]][ 0 ] >= x_max :
+203 offset_x1 = 1
+204 else :
+205 offset_x1 = 0
+206
+207 # Now check y coord
+208 if vertices [ e [ 1 ]][ 1 ] < y_min :
+209 offset_y1 = - 1
+210 elif vertices [ e [ 1 ]][ 1 ] >= y_max :
+211 offset_y1 = 1
+212 else :
+213 offset_y1 = 0
+214
+215 # Find corresponding inside vertex to the outside dest vertex
+216 if e [ 1 ] not in inside_vertex :
+217 print ( f "Error, no inside vertex found for vertex { e [ 1 ] } ." )
+218 else :
+219 idx = inside_vertex [ e [ 1 ]]
+220 edges_outside . append ( tuple ( sorted (( e [ 0 ], idx ))))
+221 if ( e [ 0 ], e [ 1 ]) not in visited and ( e [ 1 ], e [ 0 ]) not in visited :
+222 offsets_outside [( e [ 0 ], idx )] = ( offset_x1 , offset_y1 )
+223 offsets_outside [( idx , e [ 0 ])] = ( - offset_x1 , - offset_y1 )
+224 visited . append (( e [ 0 ], e [ 1 ]))
+225 visited . append (( e [ 1 ], e [ 0 ]))
+226 else :
+227 # dest in, origin out
+228 if vertices [ e [ 0 ]][ 0 ] < x_min :
+229 offset_x0 = - 1
+230 elif vertices [ e [ 0 ]][ 0 ] >= x_max :
+231 offset_x0 = 1
+232 else :
+233 offset_x0 = 0
+234
+235 if vertices [ e [ 0 ]][ 1 ] < y_min :
+236 offset_y0 = - 1
+237 elif vertices [ e [ 0 ]][ 1 ] >= y_max :
+238 offset_y0 = 1
+239 else :
+240 offset_y0 = 0
+241
+242 # Find corresponding inside vertex to the outside dest vertex
+243 if e [ 0 ] not in inside_vertex :
+244 print ( f "Error, no inside vertex found for vertex { e [ 0 ] } ." )
+245 else :
+246 idx = inside_vertex [ e [ 0 ]]
+247 edges_outside . append ( tuple ( sorted (( idx , e [ 1 ]))))
+248 if ( e [ 0 ], e [ 1 ]) not in visited and ( e [ 1 ], e [ 0 ]) not in visited :
+249 offsets_outside [( idx , e [ 1 ])] = ( - offset_x0 , - offset_y0 )
+250 offsets_outside [( e [ 1 ], idx )] = ( offset_x0 , offset_y0 )
+251 visited . append (( e [ 0 ], e [ 1 ]))
+252 visited . append (( e [ 1 ], e [ 0 ]))
+253
+254 periodic_edges = list ( set ( edges_inside )) + list ( set ( edges_outside ))
+255 offsets = offsets_inside | offsets_outside
+256
+257 periodic_faces : list [ set [ int ]] = [
+258 { inside_vertex [ i ] for i in face } for face in faces if any ( v_id in periodic_vertices_idx for v_id in face )
+259 ]
+260
+261 vertices , edges , faces = _make_he_structure (
+262 2 * width ,
+263 2 * height ,
+264 periodic_vertices_idx , # ty:ignore[invalid-argument-type]
+265 periodic_vertices_pos ,
+266 periodic_edges ,
+267 offsets ,
+268 periodic_faces ,
+269 vertices_offset = ( x_min , y_min ),
+270 )
+271
+272 pbc_mesh = cls . _create ()
+273 pbc_mesh . vertices = jnp . array ( vertices , dtype = np . float32 )
+274 pbc_mesh . edges = jnp . array ( edges , dtype = np . int32 )
+275 pbc_mesh . faces = jnp . array ( faces , dtype = np . int32 )
+276 pbc_mesh . width = 2 * width
+277 pbc_mesh . height = 2 * height
+278
+279 return pbc_mesh
+280
+281 @classmethod
+282 def create_empty ( cls ) -> Self :
+283 """Create an empty mesh. Use if you know what you're doing !"""
+284 return cls . _create ()
+285
+286 @classmethod
+287 def copy_mesh ( cls , other_mesh : Self ) -> Self :
+288 """Copy all parameters from another mesh in a new mesh."""
+289 mesh = cls . _create ()
+290 mesh . vertices = other_mesh . vertices . copy ()
+291 mesh . edges = other_mesh . edges . copy ()
+292 mesh . faces = other_mesh . faces . copy ()
+293 mesh . width = other_mesh . width
+294 mesh . height = other_mesh . height
+295 mesh . vertices_params = other_mesh . vertices_params . copy ()
+296 mesh . edges_params = other_mesh . edges_params . copy ()
+297 mesh . faces_params = other_mesh . faces_params . copy ()
+298 mesh . MAX_EDGES_IN_ANY_FACE = other_mesh . MAX_EDGES_IN_ANY_FACE
+299
+300 return mesh
+301
+302 def save_mesh ( self , path : str ) -> None :
+303 """Save mesh to a file.
+304
+305 All PBCMesh data is saved.
+306
+307 Args:
+308 path (str): Path to the saved file. The extension is .npz, a numpy format.
+309 """
+310 Path ( path ) . parent . mkdir ( parents = True , exist_ok = True )
+311 np . savez_compressed (
+312 path ,
+313 allow_pickle = False ,
+314 vertices = self . vertices ,
+315 edges = self . edges ,
+316 faces = self . faces ,
+317 width = self . width ,
+318 height = self . height ,
+319 vertices_params = self . vertices_params ,
+320 edges_params = self . edges_params ,
+321 faces_params = self . faces_params ,
+322 MAX_EDGES_IN_ANY_FACE = self . MAX_EDGES_IN_ANY_FACE ,
+323 )
+324
+325 @classmethod
+326 def load_mesh ( cls , path : str ) -> Self :
+327 """Load a mesh from a file.
+328
+329 All PBCMesh data is reloaded.
+330
+331 Args:
+332 path (str): Path to the mesh file (.npz), numpy format.
+333
+334 Returns:
+335 Mesh: the mesh loaded from the numpy .npz file.
+336 """
+337 mesh_file = np . load ( path )
+338 mesh = cls . _create ()
+339 mesh . vertices , mesh . edges , mesh . faces = (
+340 jnp . array ( mesh_file [ "vertices" ]),
+341 jnp . array ( mesh_file [ "edges" ] . reshape ( - 1 , 8 )),
+342 jnp . array ( mesh_file [ "faces" ]),
+343 )
+344 mesh . width , mesh . height = float ( mesh_file [ "width" ]), float ( mesh_file [ "height" ])
+345 mesh . vertices_params = jnp . array ( mesh_file [ "vertices_params" ])
+346 mesh . edges_params = jnp . array ( mesh_file [ "edges_params" ])
+347 mesh . faces_params = jnp . array ( mesh_file [ "faces_params" ])
+348 mesh . MAX_EDGES_IN_ANY_FACE = mesh_file [ "MAX_EDGES_IN_ANY_FACE" ]
+349 return mesh
+350
+351 def save_mesh_txt (
+352 self ,
+353 directory : str ,
+354 vertices_filename : str = "vertTable.txt" ,
+355 edges_filename : str = "heTable.txt" ,
+356 faces_filename : str = "faceTable.txt" ,
+357 vertices_params_filename : str = "vertParamsTable.txt" ,
+358 edges_params_filename : str = "heParamsTable.txt" ,
+359 faces_params_filename : str = "faceParamsTable.txt" ,
+360 constants_filename : str = "constants.txt" ,
+361 ) -> None :
+362 """Save a mesh in separate text files that can be read by numpy.
+363
+364 Only save the vertices, edges and faces, not other parameters.
+365
+366 Args:
+367 directory (str): Path to the directory where to save the files.
+368 vertices_filename (str, optional): Filename for the vertices table. Defaults to "vertTable.txt".
+369 edges_filename (str, optional): Filename for the half-edges table. Defaults to "heTable.txt".
+370 faces_filename (str, optional): Filename for the faces table. Defaults to "faceTable.txt".
+371 vertices_params_filename (str, optional): Filename for the vertices parameters table.
+372 Defaults to "vertParamsTable.txt".
+373 edges_params_filename (str, optional): Filename for the half-edges parameters table.
+374 Defaults to "heParamsTable.txt".
+375 faces_params_filename (str, optional): Filename for the faces parameters table.
+376 Defaults to "faceParamsTable.txt".
+377 constants_filename (str, optional): Filename for width/height/MAX_EDGES_IN_ANY_FACE.
+378 Defaults to "constants.txt".
+379 """
+380 dirpath = Path ( directory )
+381 dirpath . mkdir ( parents = True , exist_ok = True )
+382 np . savetxt ( dirpath / vertices_filename , self . vertices )
+383 np . savetxt ( dirpath / edges_filename , self . edges )
+384 np . savetxt ( dirpath / faces_filename , self . faces )
+385 np . savetxt ( dirpath / vertices_params_filename , self . vertices_params )
+386 np . savetxt ( dirpath / edges_params_filename , self . edges_params )
+387 np . savetxt ( dirpath / faces_params_filename , self . faces_params )
+388 with ( dirpath / constants_filename ) . open ( "w" ) as f :
+389 f . write ( f " { self . width } { self . height } { self . MAX_EDGES_IN_ANY_FACE } " )
+390
+391 @classmethod
+392 def load_mesh_txt (
+393 cls ,
+394 directory : str ,
+395 vertices_filename : str = "vertTable.txt" ,
+396 edges_filename : str = "heTable.txt" ,
+397 faces_filename : str = "faceTable.txt" ,
+398 vertices_params_filename : str = "vertParamsTable.txt" ,
+399 edges_params_filename : str = "heParamsTable.txt" ,
+400 faces_params_filename : str = "faceParamsTable.txt" ,
+401 constants_filename : str = "constants.txt" ,
+402 ) -> Self :
+403 """Load a mesh from text files.
+404
+405 Only load the vertices, edges and faces, not other parameters.
+406
+407 Args:
+408 directory (str): Directory where the text files are stored.
+409 vertices_filename (str, optional): Filename for the vertices table. Defaults to "vertTable.txt".
+410 edges_filename (str, optional): Filename for the half-edges table. Defaults to "heTable.txt".
+411 faces_filename (str, optional): Filename for the faces table. Defaults to "faceTable.txt".
+412 vertices_params_filename (str, optional): Filename for the vertices parameters table.
+413 Defaults to "vertParamsTable.txt".
+414 edges_params_filename (str, optional): Filename for the half-edges parameters table.
+415 Defaults to "heParamsTable.txt".
+416 faces_params_filename (str, optional): Filename for the faces parameters table.
+417 Defaults to "faceParamsTable.txt".
+418 constants_filename (str, optional): Filename for width/height/MAX_EDGES_IN_ANY_FACE.
+419 Defaults to "constants.txt".
+420
+421 Returns:
+422 Self: The loaded mesh.
+423 """
+424 dirpath = Path ( directory )
+425 dirpath . mkdir ( parents = True , exist_ok = True )
+426
+427 mesh = cls . _create ()
+428 mesh . vertices = jnp . array ( np . loadtxt ( dirpath / vertices_filename , dtype = np . float64 ))
+429 mesh . edges = jnp . array ( np . loadtxt ( dirpath / edges_filename , dtype = np . int64 ))
+430 mesh . faces = jnp . array ( np . loadtxt ( dirpath / faces_filename , dtype = np . int64 ))
+431 mesh . vertices_params = jnp . array ( np . loadtxt ( dirpath / vertices_params_filename , dtype = np . float64 ))
+432 mesh . edges_params = jnp . array ( np . loadtxt ( dirpath / edges_params_filename , dtype = np . int64 ))
+433 mesh . faces_params = jnp . array ( np . loadtxt ( dirpath / faces_params_filename , dtype = np . int64 ))
+434 with ( dirpath / constants_filename ) . open ( "r" ) as f :
+435 numbers = f . readline () . split ()
+436 mesh . width = float ( numbers [ 0 ])
+437 mesh . height = float ( numbers [ 1 ])
+438 mesh . MAX_EDGES_IN_ANY_FACE = int ( numbers [ 2 ])
+439 return mesh
+440
+441 def get_length ( self , half_edge_id : Array ) -> Array :
+442 """Get the length of given edges."""
+443
+444 def _get_length ( half_edge_id : Array ) -> Array :
+445 return get_length ( half_edge_id , self . vertices , self . edges , self . faces , self . width , self . height )
+446
+447 return jax . vmap ( _get_length )( half_edge_id )
+448
+449 def get_length_with_offset ( self , half_edge_id : Array ) -> Array :
+450 """Get the length of given edges along with its offsets in an array (length, offset x, offset y)."""
+451
+452 def _get_length_with_offset ( half_edge_id : Array ) -> Array :
+453 return get_length_with_offset ( half_edge_id , self . vertices , self . edges , self . faces , self . width , self . height )
+454
+455 return jax . vmap ( _get_length_with_offset )( half_edge_id )
+456
+457 def get_perimeter ( self , face_id : Array ) -> Array :
+458 """Get the perimeter of given faces."""
+459
+460 def _get_perimeter ( face_id : Array ) -> Array :
+461 return get_perimeter (
+462 face_id , self . vertices , self . edges , self . faces , self . width , self . height , self . MAX_EDGES_IN_ANY_FACE
+463 )
+464
+465 return jax . vmap ( _get_perimeter )( face_id )
+466
+467 def get_area ( self , face_id : Array ) -> Array :
+468 """Get the area of given faces."""
+469
+470 def _get_area ( face_id : Array ) -> Array :
+471 return get_area (
+472 face_id , self . vertices , self . edges , self . faces , self . width , self . height , self . MAX_EDGES_IN_ANY_FACE
+473 )
+474
+475 return jax . vmap ( _get_area )( face_id )
+476
+477 def update_boundary_conditions ( self ) -> None :
+478 """Force periodic boundary conditions again after an update.
+479
+480 Most likely you'll never have to use it yourself, as it can be made automatically with a `PbcBilevelOptimizer`.
+481 """
+482 self . vertices , self . edges , self . faces = update_pbc (
+483 self . vertices , self . edges , self . faces , self . width , self . height
+484 )
+
+
+
+ Periodic Boundary Condition on a mesh.
+
+
For a PbcMesh, vertices is a 2D array of floats of size (nb_vertices, 2) ;
+with the coordinates of the vertices (in ]0, width[ x ]0, height[ ).
+
+
edges is a 2D array of integers of size (nb_half_edges, 8), with:
+
+
+id of previous half-edge,
+id of next half-edge,
+id of twin half-edge,
+id of source vertex,
+id of target vertex,
+id of the face containing the half-edge.
+x-offset (either -1, 0 or 1) depending on the target vertex crossing a boundary.
+y-offset (either -1, 0 or 1) depending on the target vertex crossing a boundary.
+
+
+
faces is a 1D array of integers of size (nb_faces) containing the id of a half-edge belonging to this face.
+
+
+
+
+
+
+
+ PbcMesh ()
+
+ View Source
+
+
+
+
41 def __init__ ( self ) -> None :
+42 """Do not call the constructor directly, use the dedicated class methods such as:
+43
+44 - `from_random_seeds`,
+45 - `from_seeds`,
+46 - `from_image`,
+47 - `from_mask`,
+48 - `create_empty`.
+49 """ # noqa: D415
+50 super () . __init__ ()
+51
+52 self . MAX_EDGES_IN_ANY_FACE : int = 20
+53 """Optimization parameter : must be more than the estimated maximum number of edges in a face. Base value is 20.""" # noqa: E501
+
+
+
+
Do not call the constructor directly, use the dedicated class methods such as:
+
+
+
+
+
+
+
+
+ MAX_EDGES_IN_ANY_FACE : int
+
+
+
+
+
+
Optimization parameter : must be more than the estimated maximum number of edges in a face. Base value is 20.
+
+
+
+
+
+
+
+
@classmethod
+
+
def
+
from_random_seeds (cls , nb_seeds : int , width : float , height : float , random_key : int ) -> Self :
+
+
View Source
+
+
+
+
55 @classmethod
+56 def from_random_seeds ( cls , nb_seeds : int , width : float , height : float , random_key : int ) -> Self :
+57 """Create a Periodic Boundary Conditions Mesh from a Voronoi diagram based on random seeds.
+58
+59 Args:
+60 nb_seeds (int): Number of random seeds to use.
+61 width (float): Width of the rectangular domains the seeds will be in.
+62 height (float): Height of the rectangular domains the seeds will be in.
+63 random_key (int): Set the random key for reproducibility.
+64
+65 Returns:
+66 Self: The corresponding mesh.
+67 """
+68 key = jax . random . PRNGKey ( random_key )
+69 seeds = jnp . array (( width , height )) * jax . random . uniform ( key , ( nb_seeds , 2 ))
+70 return cls . from_seeds ( seeds , width , height )
+
+
+
+
Create a Periodic Boundary Conditions Mesh from a Voronoi diagram based on random seeds.
+
+
Arguments:
+
+
+nb_seeds (int): Number of random seeds to use.
+width (float): Width of the rectangular domains the seeds will be in.
+height (float): Height of the rectangular domains the seeds will be in.
+random_key (int): Set the random key for reproducibility.
+
+
+
Returns:
+
+
+ Self: The corresponding mesh.
+
+
+
+
+
+
+
+
+
@classmethod
+
+
def
+
from_seeds (cls , seeds : jax . Array , width : float , height : float ) -> Self :
+
+
View Source
+
+
+
+
72 @classmethod
+ 73 def from_seeds ( cls , seeds : Array , width : float , height : float ) -> Self :
+ 74 """Create a Periodic Boundary Conditions Mesh from a Voronoi diagram based on a list of seeds.
+ 75
+ 76 The seeds are assumed to have positive x and y positions.
+ 77
+ 78 Args:
+ 79 seeds (Array[float32]): jax float array of seed positions of shape (nbSeeds, 2).
+ 80 width (float): width of the box containing the seeds.
+ 81 height (float): height of the box containing the seeds.
+ 82 """
+ 83 (
+ 84 periodic_voronoi_vertices_idx ,
+ 85 periodic_voronoi_vertices_pos ,
+ 86 periodic_voronoi_edges ,
+ 87 offsets ,
+ 88 periodic_voronoi_faces ,
+ 89 ) = _make_periodic ( seeds , width , height )
+ 90
+ 91 vertices , edges , faces = _make_he_structure (
+ 92 width ,
+ 93 height ,
+ 94 periodic_voronoi_vertices_idx ,
+ 95 periodic_voronoi_vertices_pos ,
+ 96 periodic_voronoi_edges ,
+ 97 offsets ,
+ 98 periodic_voronoi_faces ,
+ 99 )
+100
+101 pbc_mesh = cls . _create ()
+102 pbc_mesh . vertices = jnp . array ( vertices , dtype = np . float32 )
+103 pbc_mesh . edges = jnp . array ( edges , dtype = np . int32 )
+104 pbc_mesh . faces = jnp . array ( faces , dtype = np . int32 )
+105 pbc_mesh . width = width
+106 pbc_mesh . height = height
+107
+108 return pbc_mesh
+
+
+
+
Create a Periodic Boundary Conditions Mesh from a Voronoi diagram based on a list of seeds.
+
+
The seeds are assumed to have positive x and y positions.
+
+
Arguments:
+
+
+seeds (Array[float32]): jax float array of seed positions of shape (nbSeeds, 2).
+width (float): width of the box containing the seeds.
+height (float): height of the box containing the seeds.
+
+
+
+
+
+
+
+
+
@classmethod
+
+
def
+
from_image ( cls , image : numpy . ndarray [ typing . Any , numpy . dtype [ + _ScalarType_co ]] ) -> Self :
+
+
View Source
+
+
+
+
110 @classmethod
+111 def from_image (
+112 cls ,
+113 image : NDArray ,
+114 ) -> Self :
+115 """Create a rudimentary mesh with periodic boundary conditions from an image.
+116
+117 To do that, we perform a segmentation using Cellpose and we try to fill the holes.
+118 The result will probably be imperfect and it will always be better if you
+119 provide directly a mask (with no holes) with the function "periodic_from_mask".
+120
+121 The mask is made periodic by mirroring its edges.
+122
+123 Args:
+124 image (NDArray): The image which will act as a template for the mesh.
+125
+126 Returns:
+127 Self: The corresponding mesh.
+128 """
+129 return cls . from_mask ( mask_from_image ( image ))
+
+
+
+
Create a rudimentary mesh with periodic boundary conditions from an image.
+
+
To do that, we perform a segmentation using Cellpose and we try to fill the holes.
+The result will probably be imperfect and it will always be better if you
+provide directly a mask (with no holes) with the function "periodic_from_mask".
+
+
The mask is made periodic by mirroring its edges.
+
+
Arguments:
+
+
+image (NDArray): The image which will act as a template for the mesh.
+
+
+
Returns:
+
+
+ Self: The corresponding mesh.
+
+
+
+
+
+
+
+
+
@classmethod
+
+
def
+
from_mask ( cls , mask : numpy . ndarray [ typing . Any , numpy . dtype [ + _ScalarType_co ]] ) -> Self :
+
+
View Source
+
+
+
+
131 @classmethod
+132 def from_mask ( # noqa: C901
+133 cls ,
+134 mask : NDArray ,
+135 ) -> Self :
+136 """Create a rudimentary mesh with periodic boundary conditions from a mask with no holes.
+137
+138 The mask is made periodic by mirroring its edges.
+139
+140 Args:
+141 mask (NDArray): The mask with no holes which will act as a template for the mesh.
+142
+143 Returns:
+144 Self: The corresponding mesh.
+145 """
+146 padded_mask = pad ( mask , save = False , output_path = "refined_and_padded_image.tiff" )
+147 # Find vertices, edges, faces
+148 vertices , edges , faces = find_vertices_edges_faces ( padded_mask )
+149
+150 # imread tiff = Y is the first axis, X the second.
+151 height : int = mask . shape [ 0 ] # original image length. Padded is 3 times bigger.
+152 y_min = height / 2
+153 y_max = 2 * height + ( height / 2 )
+154 width : int = mask . shape [ 1 ]
+155 x_min = width / 2
+156 x_max = 2 * width + ( width / 2 )
+157
+158 col0_mask = ( vertices [:, 0 ] >= x_min ) & ( vertices [:, 0 ] < x_max )
+159 col1_mask = ( vertices [:, 1 ] >= y_min ) & ( vertices [:, 1 ] < y_max )
+160
+161 periodic_vertices_idx = np . arange ( len ( vertices ))[ col0_mask & col1_mask ]
+162 periodic_vertices_pos = vertices [ col0_mask & col1_mask ]
+163
+164 # store map between vertex id -> inside vertex id
+165 inside_vertex : dict [ int , int ] = { idx : idx for idx in periodic_vertices_idx }
+166 for i , vertex in enumerate ( vertices ):
+167 if i not in periodic_vertices_idx :
+168 x , y = vertex
+169 if x < x_min :
+170 x += 2 * width
+171 elif x >= x_max :
+172 x -= 2 * width
+173
+174 if y < y_min :
+175 y += 2 * height
+176 elif y >= y_max :
+177 y -= 2 * height
+178
+179 # Find corresponding inside vertex to the outside dest vertex
+180 for idx , pos in zip ( periodic_vertices_idx , periodic_vertices_pos , strict = True ):
+181 if np . max ( np . abs ( pos - [ x , y ])) < 1 :
+182 inside_vertex [ i ] = idx
+183 break
+184
+185 edges_inside = []
+186 edges_outside = []
+187 offsets_inside = {}
+188 offsets_outside = {}
+189 visited = []
+190
+191 for e in edges :
+192 if e [ 0 ] in periodic_vertices_idx and e [ 1 ] in periodic_vertices_idx :
+193 edges_inside . append ( tuple ( sorted (( e [ 0 ], e [ 1 ]))))
+194 offsets_inside [( e [ 0 ], e [ 1 ])] = ( 0 , 0 )
+195 offsets_inside [( e [ 1 ], e [ 0 ])] = ( 0 , 0 )
+196 elif bool ( e [ 0 ] in periodic_vertices_idx ) != bool ( e [ 1 ] in periodic_vertices_idx ):
+197 if e [ 0 ] in periodic_vertices_idx :
+198 # origin in, dest out
+199 # check x coord
+200 if vertices [ e [ 1 ]][ 0 ] < x_min :
+201 offset_x1 = - 1
+202 elif vertices [ e [ 1 ]][ 0 ] >= x_max :
+203 offset_x1 = 1
+204 else :
+205 offset_x1 = 0
+206
+207 # Now check y coord
+208 if vertices [ e [ 1 ]][ 1 ] < y_min :
+209 offset_y1 = - 1
+210 elif vertices [ e [ 1 ]][ 1 ] >= y_max :
+211 offset_y1 = 1
+212 else :
+213 offset_y1 = 0
+214
+215 # Find corresponding inside vertex to the outside dest vertex
+216 if e [ 1 ] not in inside_vertex :
+217 print ( f "Error, no inside vertex found for vertex { e [ 1 ] } ." )
+218 else :
+219 idx = inside_vertex [ e [ 1 ]]
+220 edges_outside . append ( tuple ( sorted (( e [ 0 ], idx ))))
+221 if ( e [ 0 ], e [ 1 ]) not in visited and ( e [ 1 ], e [ 0 ]) not in visited :
+222 offsets_outside [( e [ 0 ], idx )] = ( offset_x1 , offset_y1 )
+223 offsets_outside [( idx , e [ 0 ])] = ( - offset_x1 , - offset_y1 )
+224 visited . append (( e [ 0 ], e [ 1 ]))
+225 visited . append (( e [ 1 ], e [ 0 ]))
+226 else :
+227 # dest in, origin out
+228 if vertices [ e [ 0 ]][ 0 ] < x_min :
+229 offset_x0 = - 1
+230 elif vertices [ e [ 0 ]][ 0 ] >= x_max :
+231 offset_x0 = 1
+232 else :
+233 offset_x0 = 0
+234
+235 if vertices [ e [ 0 ]][ 1 ] < y_min :
+236 offset_y0 = - 1
+237 elif vertices [ e [ 0 ]][ 1 ] >= y_max :
+238 offset_y0 = 1
+239 else :
+240 offset_y0 = 0
+241
+242 # Find corresponding inside vertex to the outside dest vertex
+243 if e [ 0 ] not in inside_vertex :
+244 print ( f "Error, no inside vertex found for vertex { e [ 0 ] } ." )
+245 else :
+246 idx = inside_vertex [ e [ 0 ]]
+247 edges_outside . append ( tuple ( sorted (( idx , e [ 1 ]))))
+248 if ( e [ 0 ], e [ 1 ]) not in visited and ( e [ 1 ], e [ 0 ]) not in visited :
+249 offsets_outside [( idx , e [ 1 ])] = ( - offset_x0 , - offset_y0 )
+250 offsets_outside [( e [ 1 ], idx )] = ( offset_x0 , offset_y0 )
+251 visited . append (( e [ 0 ], e [ 1 ]))
+252 visited . append (( e [ 1 ], e [ 0 ]))
+253
+254 periodic_edges = list ( set ( edges_inside )) + list ( set ( edges_outside ))
+255 offsets = offsets_inside | offsets_outside
+256
+257 periodic_faces : list [ set [ int ]] = [
+258 { inside_vertex [ i ] for i in face } for face in faces if any ( v_id in periodic_vertices_idx for v_id in face )
+259 ]
+260
+261 vertices , edges , faces = _make_he_structure (
+262 2 * width ,
+263 2 * height ,
+264 periodic_vertices_idx , # ty:ignore[invalid-argument-type]
+265 periodic_vertices_pos ,
+266 periodic_edges ,
+267 offsets ,
+268 periodic_faces ,
+269 vertices_offset = ( x_min , y_min ),
+270 )
+271
+272 pbc_mesh = cls . _create ()
+273 pbc_mesh . vertices = jnp . array ( vertices , dtype = np . float32 )
+274 pbc_mesh . edges = jnp . array ( edges , dtype = np . int32 )
+275 pbc_mesh . faces = jnp . array ( faces , dtype = np . int32 )
+276 pbc_mesh . width = 2 * width
+277 pbc_mesh . height = 2 * height
+278
+279 return pbc_mesh
+
+
+
+
Create a rudimentary mesh with periodic boundary conditions from a mask with no holes.
+
+
The mask is made periodic by mirroring its edges.
+
+
Arguments:
+
+
+mask (NDArray): The mask with no holes which will act as a template for the mesh.
+
+
+
Returns:
+
+
+ Self: The corresponding mesh.
+
+
+
+
+
+
+
+
+
@classmethod
+
+
def
+
create_empty (cls ) -> Self :
+
+
View Source
+
+
+
+
281 @classmethod
+282 def create_empty ( cls ) -> Self :
+283 """Create an empty mesh. Use if you know what you're doing !"""
+284 return cls . _create ()
+
+
+
+
Create an empty mesh. Use if you know what you're doing !
+
+
+
+
+
+
+
+
@classmethod
+
+
def
+
copy_mesh (cls , other_mesh : Self ) -> Self :
+
+
View Source
+
+
+
+
286 @classmethod
+287 def copy_mesh ( cls , other_mesh : Self ) -> Self :
+288 """Copy all parameters from another mesh in a new mesh."""
+289 mesh = cls . _create ()
+290 mesh . vertices = other_mesh . vertices . copy ()
+291 mesh . edges = other_mesh . edges . copy ()
+292 mesh . faces = other_mesh . faces . copy ()
+293 mesh . width = other_mesh . width
+294 mesh . height = other_mesh . height
+295 mesh . vertices_params = other_mesh . vertices_params . copy ()
+296 mesh . edges_params = other_mesh . edges_params . copy ()
+297 mesh . faces_params = other_mesh . faces_params . copy ()
+298 mesh . MAX_EDGES_IN_ANY_FACE = other_mesh . MAX_EDGES_IN_ANY_FACE
+299
+300 return mesh
+
+
+
+
Copy all parameters from another mesh in a new mesh.
+
+
+
+
+
+
+
+
+ def
+ save_mesh (self , path : str ) -> None :
+
+ View Source
+
+
+
+
302 def save_mesh ( self , path : str ) -> None :
+303 """Save mesh to a file.
+304
+305 All PBCMesh data is saved.
+306
+307 Args:
+308 path (str): Path to the saved file. The extension is .npz, a numpy format.
+309 """
+310 Path ( path ) . parent . mkdir ( parents = True , exist_ok = True )
+311 np . savez_compressed (
+312 path ,
+313 allow_pickle = False ,
+314 vertices = self . vertices ,
+315 edges = self . edges ,
+316 faces = self . faces ,
+317 width = self . width ,
+318 height = self . height ,
+319 vertices_params = self . vertices_params ,
+320 edges_params = self . edges_params ,
+321 faces_params = self . faces_params ,
+322 MAX_EDGES_IN_ANY_FACE = self . MAX_EDGES_IN_ANY_FACE ,
+323 )
+
+
+
+
Save mesh to a file.
+
+
All PBCMesh data is saved.
+
+
Arguments:
+
+
+path (str): Path to the saved file. The extension is .npz, a numpy format.
+
+
+
+
+
+
+
+
+
@classmethod
+
+
def
+
load_mesh (cls , path : str ) -> Self :
+
+
View Source
+
+
+
+
325 @classmethod
+326 def load_mesh ( cls , path : str ) -> Self :
+327 """Load a mesh from a file.
+328
+329 All PBCMesh data is reloaded.
+330
+331 Args:
+332 path (str): Path to the mesh file (.npz), numpy format.
+333
+334 Returns:
+335 Mesh: the mesh loaded from the numpy .npz file.
+336 """
+337 mesh_file = np . load ( path )
+338 mesh = cls . _create ()
+339 mesh . vertices , mesh . edges , mesh . faces = (
+340 jnp . array ( mesh_file [ "vertices" ]),
+341 jnp . array ( mesh_file [ "edges" ] . reshape ( - 1 , 8 )),
+342 jnp . array ( mesh_file [ "faces" ]),
+343 )
+344 mesh . width , mesh . height = float ( mesh_file [ "width" ]), float ( mesh_file [ "height" ])
+345 mesh . vertices_params = jnp . array ( mesh_file [ "vertices_params" ])
+346 mesh . edges_params = jnp . array ( mesh_file [ "edges_params" ])
+347 mesh . faces_params = jnp . array ( mesh_file [ "faces_params" ])
+348 mesh . MAX_EDGES_IN_ANY_FACE = mesh_file [ "MAX_EDGES_IN_ANY_FACE" ]
+349 return mesh
+
+
+
+
Load a mesh from a file.
+
+
All PBCMesh data is reloaded.
+
+
Arguments:
+
+
+path (str): Path to the mesh file (.npz), numpy format.
+
+
+
Returns:
+
+
+ Mesh: the mesh loaded from the numpy .npz file.
+
+
+
+
+
+
+
+
+
+ def
+ save_mesh_txt ( self , directory : str , vertices_filename : str = 'vertTable.txt' , edges_filename : str = 'heTable.txt' , faces_filename : str = 'faceTable.txt' , vertices_params_filename : str = 'vertParamsTable.txt' , edges_params_filename : str = 'heParamsTable.txt' , faces_params_filename : str = 'faceParamsTable.txt' , constants_filename : str = 'constants.txt' ) -> None :
+
+ View Source
+
+
+
+
351 def save_mesh_txt (
+352 self ,
+353 directory : str ,
+354 vertices_filename : str = "vertTable.txt" ,
+355 edges_filename : str = "heTable.txt" ,
+356 faces_filename : str = "faceTable.txt" ,
+357 vertices_params_filename : str = "vertParamsTable.txt" ,
+358 edges_params_filename : str = "heParamsTable.txt" ,
+359 faces_params_filename : str = "faceParamsTable.txt" ,
+360 constants_filename : str = "constants.txt" ,
+361 ) -> None :
+362 """Save a mesh in separate text files that can be read by numpy.
+363
+364 Only save the vertices, edges and faces, not other parameters.
+365
+366 Args:
+367 directory (str): Path to the directory where to save the files.
+368 vertices_filename (str, optional): Filename for the vertices table. Defaults to "vertTable.txt".
+369 edges_filename (str, optional): Filename for the half-edges table. Defaults to "heTable.txt".
+370 faces_filename (str, optional): Filename for the faces table. Defaults to "faceTable.txt".
+371 vertices_params_filename (str, optional): Filename for the vertices parameters table.
+372 Defaults to "vertParamsTable.txt".
+373 edges_params_filename (str, optional): Filename for the half-edges parameters table.
+374 Defaults to "heParamsTable.txt".
+375 faces_params_filename (str, optional): Filename for the faces parameters table.
+376 Defaults to "faceParamsTable.txt".
+377 constants_filename (str, optional): Filename for width/height/MAX_EDGES_IN_ANY_FACE.
+378 Defaults to "constants.txt".
+379 """
+380 dirpath = Path ( directory )
+381 dirpath . mkdir ( parents = True , exist_ok = True )
+382 np . savetxt ( dirpath / vertices_filename , self . vertices )
+383 np . savetxt ( dirpath / edges_filename , self . edges )
+384 np . savetxt ( dirpath / faces_filename , self . faces )
+385 np . savetxt ( dirpath / vertices_params_filename , self . vertices_params )
+386 np . savetxt ( dirpath / edges_params_filename , self . edges_params )
+387 np . savetxt ( dirpath / faces_params_filename , self . faces_params )
+388 with ( dirpath / constants_filename ) . open ( "w" ) as f :
+389 f . write ( f " { self . width } { self . height } { self . MAX_EDGES_IN_ANY_FACE } " )
+
+
+
+
Save a mesh in separate text files that can be read by numpy.
+
+
Only save the vertices, edges and faces, not other parameters.
+
+
Arguments:
+
+
+directory (str): Path to the directory where to save the files.
+vertices_filename (str, optional): Filename for the vertices table. Defaults to "vertTable.txt".
+edges_filename (str, optional): Filename for the half-edges table. Defaults to "heTable.txt".
+faces_filename (str, optional): Filename for the faces table. Defaults to "faceTable.txt".
+vertices_params_filename (str, optional): Filename for the vertices parameters table.
+Defaults to "vertParamsTable.txt".
+edges_params_filename (str, optional): Filename for the half-edges parameters table.
+Defaults to "heParamsTable.txt".
+faces_params_filename (str, optional): Filename for the faces parameters table.
+Defaults to "faceParamsTable.txt".
+constants_filename (str, optional): Filename for width/height/MAX_EDGES_IN_ANY_FACE.
+Defaults to "constants.txt".
+
+
+
+
+
+
+
+
+
@classmethod
+
+
def
+
load_mesh_txt ( cls , directory : str , vertices_filename : str = 'vertTable.txt' , edges_filename : str = 'heTable.txt' , faces_filename : str = 'faceTable.txt' , vertices_params_filename : str = 'vertParamsTable.txt' , edges_params_filename : str = 'heParamsTable.txt' , faces_params_filename : str = 'faceParamsTable.txt' , constants_filename : str = 'constants.txt' ) -> Self :
+
+
View Source
+
+
+
+
391 @classmethod
+392 def load_mesh_txt (
+393 cls ,
+394 directory : str ,
+395 vertices_filename : str = "vertTable.txt" ,
+396 edges_filename : str = "heTable.txt" ,
+397 faces_filename : str = "faceTable.txt" ,
+398 vertices_params_filename : str = "vertParamsTable.txt" ,
+399 edges_params_filename : str = "heParamsTable.txt" ,
+400 faces_params_filename : str = "faceParamsTable.txt" ,
+401 constants_filename : str = "constants.txt" ,
+402 ) -> Self :
+403 """Load a mesh from text files.
+404
+405 Only load the vertices, edges and faces, not other parameters.
+406
+407 Args:
+408 directory (str): Directory where the text files are stored.
+409 vertices_filename (str, optional): Filename for the vertices table. Defaults to "vertTable.txt".
+410 edges_filename (str, optional): Filename for the half-edges table. Defaults to "heTable.txt".
+411 faces_filename (str, optional): Filename for the faces table. Defaults to "faceTable.txt".
+412 vertices_params_filename (str, optional): Filename for the vertices parameters table.
+413 Defaults to "vertParamsTable.txt".
+414 edges_params_filename (str, optional): Filename for the half-edges parameters table.
+415 Defaults to "heParamsTable.txt".
+416 faces_params_filename (str, optional): Filename for the faces parameters table.
+417 Defaults to "faceParamsTable.txt".
+418 constants_filename (str, optional): Filename for width/height/MAX_EDGES_IN_ANY_FACE.
+419 Defaults to "constants.txt".
+420
+421 Returns:
+422 Self: The loaded mesh.
+423 """
+424 dirpath = Path ( directory )
+425 dirpath . mkdir ( parents = True , exist_ok = True )
+426
+427 mesh = cls . _create ()
+428 mesh . vertices = jnp . array ( np . loadtxt ( dirpath / vertices_filename , dtype = np . float64 ))
+429 mesh . edges = jnp . array ( np . loadtxt ( dirpath / edges_filename , dtype = np . int64 ))
+430 mesh . faces = jnp . array ( np . loadtxt ( dirpath / faces_filename , dtype = np . int64 ))
+431 mesh . vertices_params = jnp . array ( np . loadtxt ( dirpath / vertices_params_filename , dtype = np . float64 ))
+432 mesh . edges_params = jnp . array ( np . loadtxt ( dirpath / edges_params_filename , dtype = np . int64 ))
+433 mesh . faces_params = jnp . array ( np . loadtxt ( dirpath / faces_params_filename , dtype = np . int64 ))
+434 with ( dirpath / constants_filename ) . open ( "r" ) as f :
+435 numbers = f . readline () . split ()
+436 mesh . width = float ( numbers [ 0 ])
+437 mesh . height = float ( numbers [ 1 ])
+438 mesh . MAX_EDGES_IN_ANY_FACE = int ( numbers [ 2 ])
+439 return mesh
+
+
+
+
Load a mesh from text files.
+
+
Only load the vertices, edges and faces, not other parameters.
+
+
Arguments:
+
+
+directory (str): Directory where the text files are stored.
+vertices_filename (str, optional): Filename for the vertices table. Defaults to "vertTable.txt".
+edges_filename (str, optional): Filename for the half-edges table. Defaults to "heTable.txt".
+faces_filename (str, optional): Filename for the faces table. Defaults to "faceTable.txt".
+vertices_params_filename (str, optional): Filename for the vertices parameters table.
+Defaults to "vertParamsTable.txt".
+edges_params_filename (str, optional): Filename for the half-edges parameters table.
+Defaults to "heParamsTable.txt".
+faces_params_filename (str, optional): Filename for the faces parameters table.
+Defaults to "faceParamsTable.txt".
+constants_filename (str, optional): Filename for width/height/MAX_EDGES_IN_ANY_FACE.
+Defaults to "constants.txt".
+
+
+
Returns:
+
+
+ Self: The loaded mesh.
+
+
+
+
+
+
+
+
+
+ def
+ get_length (self , half_edge_id : jax . Array ) -> jax . Array :
+
+ View Source
+
+
+
+
441 def get_length ( self , half_edge_id : Array ) -> Array :
+442 """Get the length of given edges."""
+443
+444 def _get_length ( half_edge_id : Array ) -> Array :
+445 return get_length ( half_edge_id , self . vertices , self . edges , self . faces , self . width , self . height )
+446
+447 return jax . vmap ( _get_length )( half_edge_id )
+
+
+
+
Get the length of given edges.
+
+
+
+
+
+
+
+
+ def
+ get_length_with_offset (self , half_edge_id : jax . Array ) -> jax . Array :
+
+ View Source
+
+
+
+
449 def get_length_with_offset ( self , half_edge_id : Array ) -> Array :
+450 """Get the length of given edges along with its offsets in an array (length, offset x, offset y)."""
+451
+452 def _get_length_with_offset ( half_edge_id : Array ) -> Array :
+453 return get_length_with_offset ( half_edge_id , self . vertices , self . edges , self . faces , self . width , self . height )
+454
+455 return jax . vmap ( _get_length_with_offset )( half_edge_id )
+
+
+
+
Get the length of given edges along with its offsets in an array (length, offset x, offset y).
+
+
+
+
+
+
+
+
+ def
+ get_perimeter (self , face_id : jax . Array ) -> jax . Array :
+
+ View Source
+
+
+
+
457 def get_perimeter ( self , face_id : Array ) -> Array :
+458 """Get the perimeter of given faces."""
+459
+460 def _get_perimeter ( face_id : Array ) -> Array :
+461 return get_perimeter (
+462 face_id , self . vertices , self . edges , self . faces , self . width , self . height , self . MAX_EDGES_IN_ANY_FACE
+463 )
+464
+465 return jax . vmap ( _get_perimeter )( face_id )
+
+
+
+
Get the perimeter of given faces.
+
+
+
+
+
+
+
+
+ def
+ get_area (self , face_id : jax . Array ) -> jax . Array :
+
+ View Source
+
+
+
+
467 def get_area ( self , face_id : Array ) -> Array :
+468 """Get the area of given faces."""
+469
+470 def _get_area ( face_id : Array ) -> Array :
+471 return get_area (
+472 face_id , self . vertices , self . edges , self . faces , self . width , self . height , self . MAX_EDGES_IN_ANY_FACE
+473 )
+474
+475 return jax . vmap ( _get_area )( face_id )
+
+
+
+
Get the area of given faces.
+
+
+
+
+
+
+
+
+ def
+ update_boundary_conditions (self ) -> None :
+
+ View Source
+
+
+
+
477 def update_boundary_conditions ( self ) -> None :
+478 """Force periodic boundary conditions again after an update.
+479
+480 Most likely you'll never have to use it yourself, as it can be made automatically with a `PbcBilevelOptimizer`.
+481 """
+482 self . vertices , self . edges , self . faces = update_pbc (
+483 self . vertices , self . edges , self . faces , self . width , self . height
+484 )
+
+
+
+
Force periodic boundary conditions again after an update.
+
+
Most likely you'll never have to use it yourself, as it can be made automatically with a PbcBilevelOptimizer .
+
+
+
+
+
+
+
+
+
+
class
+
BoundedMesh (vertax.Mesh ):
+
+ View Source
+
+
+
+ 25 class BoundedMesh ( Mesh ):
+ 26 """Bounded mesh with arc circles for boundary cells.
+ 27
+ 28 For a BoundedMesh, `vertices` is a 2D array of floats of size (nb_vertices, 2) ;
+ 29 with the coordinates of the vertices (in ]0, width[ x ]0, height[ ).
+ 30
+ 31 `edges` is a 2D array of integers of size (nb_half_edges, 8), with:
+ 32
+ 33 - id of previous half-edge,
+ 34 - id of next half-edge,
+ 35 - id of twin half-edge,
+ 36 - id of source vertex + 2 if current half-edge is an inside edge, else 0,
+ 37 - id of target vertex + 2 if current half-edge is an inside edge, else 1,
+ 38 - id of source vertex + 2 if current half-edge is an outside edge, else 0,
+ 39 - id of target vertex + 2 if current half-edge is an outside edge, else 1,
+ 40 - id of the face containing the half-edge.
+ 41
+ 42 `faces` is a 1D array of integers of size (nb_faces) containing the id of a half-edge belonging to this face.
+ 43
+ 44 `angles` is a 1D array of floats of size (nb_angles), with the angles sustaining the arcs of the free interfaces ;
+ 45 between 0 and PI / 2.
+ 46 """
+ 47
+ 48 def __init__ ( self ) -> None :
+ 49 """Do not call the constructor."""
+ 50 super () . __init__ ()
+ 51 self . angles : Array = jnp . array ([])
+ 52 """Angle sustaining the arced free interfaces. Between 0 and PI / 2."""
+ 53
+ 54 @property
+ 55 def nb_angles ( self ) -> int :
+ 56 """Get the number of angles (free interfaces) of the mesh."""
+ 57 return len ( self . angles )
+ 58
+ 59 @classmethod
+ 60 def from_random_seeds ( cls , nb_seeds : int , width : float , height : float , random_key : int , nb_fates : int = 2 ) -> Self :
+ 61 """Create a bounded Mesh from random seeds, based on a Voronoi diagram with arced free interfaces.
+ 62
+ 63 Args:
+ 64 nb_seeds (int): Number of random seeds to use.
+ 65 width (float): width of the box containing the seeds.
+ 66 height (float): height of the box containing the seeds.
+ 67 random_key (int): seed for a random number generator to add new seeds if needed, decide on cell fates...
+ 68 nb_fates (int, default=2): number of possible different fate marker for a cell.
+ 69
+ 70 Returns:
+ 71 Self: The corresponding mesh.
+ 72 """
+ 73 rng = np . random . default_rng ( seed = random_key )
+ 74 seeds = rng . random (( nb_seeds , 2 )) * ( width , height )
+ 75 return cls . from_seeds ( seeds , width , height , random_key , nb_fates )
+ 76
+ 77 @classmethod
+ 78 def from_seeds ( cls , seeds : NDArray , width : float , height : float , random_key : int , nb_fates : int = 2 ) -> Self : # noqa: C901
+ 79 """Create a bounded Mesh from a list of given seeds.
+ 80
+ 81 The seeds are assumed to have x-coordinate in ]0, width[ and y-coordinate in ]0, height[.
+ 82 Note that the final mesh might not use your seeds if they don't work to create a correct
+ 83 bounded mesh via our method.
+ 84
+ 85 Args:
+ 86 seeds (Array[float32]): jax float array of seed positions of shape (nbSeeds, 2).
+ 87 width (float): width of the box containing the seeds.
+ 88 height (float): height of the box containing the seeds.
+ 89 random_key (int): seed for a random number generator to add new seeds if needed, decide on cell fates...
+ 90 nb_fates (int, default=2): number of possible different fate marker for a cell.
+ 91 """
+ 92 rng = np . random . default_rng ( seed = random_key )
+ 93 n_cells = len ( seeds ) # starting number of seeds must be equal to the desired number of cells (faces)
+ 94
+ 95 # We'll try to construct a Voronoi diagram with n_cells closed (bounded) cells.
+ 96 # This will not work with exactly n_cells seeds because there will be unbounded cells.
+ 97 # If the number of bounded cells is insufficient, we add a new seed to the list.
+ 98 # If there is too many bounded cells, we retry with entirely new seeds.
+ 99 # We also check that the bounded cells are connected.
+100 while True :
+101 success = 0 # if 1 : not enough bounded cells. If 2 : too many bounded cells.
+102
+103 # Create the Voronoi diagrams from current seed list.
+104 voronoi = Voronoi ( seeds )
+105 vertices = voronoi . vertices
+106 edges = voronoi . ridge_vertices
+107 faces = voronoi . regions # regions = faces = cells
+108
+109 # We count the number of bounded cells and the connectivity of vertices.
+110 inbound_faces = []
+111 inbound_vertices = np . zeros ( vertices . shape [ 0 ], dtype = np . int32 )
+112 for face in faces :
+113 if face and all ( item > - 1 for item in face ): # the face must not be an empty list
+114 face_vertices_positions = vertices [ face ]
+115 # We check that all of the face's vertices are in a box [0, width]x[0, height]
+116 if (
+117 np . all ( face_vertices_positions [:, 0 ] < width )
+118 and np . all ( face_vertices_positions [:, 1 ] < height )
+119 and np . all ( face_vertices_positions > 0 )
+120 ):
+121 inbound_faces . append ( face ) # the face is bounded
+122 inbound_vertices [ face ] += 1 # +1 to the connectivity of the face's vertices.
+123
+124 # getting rid of faces connected to a single other inbound face
+125 # (these can be problematic and lead to many special cases later on)
+126 while True :
+127 num_infaces = len ( inbound_faces )
+128 del_count = 0
+129 for i , face in enumerate ( reversed ( inbound_faces )):
+130 # only 2 (or less (not sure it's possible)) vertices of the face are shared with other faces :
+131 # that means it is connected to one other bounded face only -> We remove it.
+132 if np . sum ( inbound_vertices [ face ] > 1 ) <= 2 :
+133 inbound_vertices [ face ] -= 1
+134 del inbound_faces [ num_infaces - i - 1 ]
+135 del_count += 1
+136 # Removing one face can possibly alter other faces so we might do another loop.
+137 # We stop when there is no more face to remove.
+138 if del_count == 0 :
+139 break
+140
+141 # Check that we have the correct number of cells.
+142 if num_infaces < n_cells :
+143 success = 1
+144 elif num_infaces > n_cells :
+145 success = 2
+146 else :
+147 # There is exactly n_cells connected bounded faces.
+148 # Now, it is possible that a bounded face has vertices or edges
+149 # that are not shared with other faces. We get rid of those,
+150 # in order to have only one exterior edge that will be an arc circle.
+151 for i , face in enumerate ( inbound_faces ):
+152 useful_vertices = [] # List of the face vertices that are shared with other.
+153 # (We'll keep them and call them "useful").
+154 extra_edges = [] # List of edges to replace what we have removed
+155 last_useful = - 1 # ID of the last "useful" vertex. -1 at the beginning (we'll treat that case)
+156 new_edge = [] # New edge that will replace current vertices we're trying to remove
+157 incomplete_new_edge = False # State boolean : are we replacing vertices right now ?
+158 for vertex in face :
+159 if inbound_vertices [ vertex ] == 1 : # We found a vertex that is not shared with other faces.
+160 # We plan to remove it by not adding it to the useful vertices list,
+161 # and by creating a new edge from last useful vertex to the next one.
+162 if not incomplete_new_edge : # Detect if we're not already in the incomplete edge state
+163 new_edge = [] # re-init
+164 new_edge . append ( last_useful )
+165 incomplete_new_edge = True # Move to incomplete edge state.
+166 else : # We found a useful vertex
+167 useful_vertices . append ( vertex )
+168 last_useful = vertex
+169 if incomplete_new_edge : # If in incomplete edge state we can finally close the new edge.
+170 new_edge . append ( vertex )
+171 extra_edges . append ( new_edge )
+172 incomplete_new_edge = False
+173 # After looping through the vertices of the face, we need to take care of
+174 # two special cases : the first or the last vertex is not shared.
+175 if extra_edges and extra_edges [ 0 ][ 0 ] == - 1 :
+176 extra_edges [ 0 ][ 0 ] = useful_vertices [ - 1 ]
+177 elif incomplete_new_edge :
+178 new_edge . append ( useful_vertices [ 0 ])
+179 extra_edges . append ( new_edge )
+180 # The extra edges are added to the list of all edges
+181 edges . extend ( extra_edges )
+182 inbound_faces [ i ] = tuple (
+183 sorted ( useful_vertices )
+184 ) # And the face itself is replaced by only the useful vertices.
+185 # Note that the vertices here are not ordered in clockwise or counterclockwise order anymore.
+186 useful_vertices_set = set ( np . where ( inbound_vertices > 1 )[ 0 ]) # We filter the useful vertices.
+187
+188 # HALF EDGE DATA STRUCTURE
+189 # Filter edges with useful vertices only.
+190 useful_edges = [ tuple ( sorted ( e )) for e in edges if set ( e ) . issubset ( useful_vertices_set )]
+191
+192 # failing to abide by the following relation results in disconnected topologies
+193 if len ( useful_edges ) != ( n_cells - 1 ) * 3 :
+194 success = 2 # Case : we want to restart with new seeds because current solution is not OK.
+195 else :
+196 # We construct the half-edges.
+197 half_edges = []
+198 for e in useful_edges :
+199 half_edges . append ( e )
+200 # reciprocating edges
+201 half_edges . append (( e [ 1 ], e [ 0 ]))
+202
+203 # finding clockwise (or counterclockwise) half edge set for each face,
+204 # as we broke it earlier.
+205 ordered_edges_inbound_faces = []
+206 for face in inbound_faces :
+207 # Find all edges fon this face and we'll loow through them to order them.
+208 edges_face = [( f1 , f2 ) for f1 in face for f2 in face if ( f1 , f2 ) in useful_edges ]
+209
+210 i = 0
+211 start_edge = edges_face [ i ]
+212 ordered_face = [ start_edge ]
+213 e = start_edge
+214 visited = [ e ]
+215 while sorted ( edges_face ) != sorted ( visited ):
+216 if e [ 0 ] == start_edge [ 1 ] and e not in visited :
+217 ordered_face . append ( e )
+218 start_edge = e
+219 visited . append ( e )
+220 # We must be careful because some edges might be in the wrong order.
+221 if e [ 1 ] == start_edge [ 1 ] and e not in visited :
+222 ordered_face . append (( e [ 1 ], e [ 0 ]))
+223 start_edge = ( e [ 1 ], e [ 0 ])
+224 visited . append ( e )
+225 i += 1
+226 e = edges_face [ i % len ( edges_face )]
+227
+228 # Sanity check : do we have a correct ordering ?
+229 order = 0
+230 for e in ordered_face :
+231 idx0 = e [ 0 ]
+232 idx1 = e [ 1 ]
+233
+234 order += ( vertices [ idx1 ][ 0 ] - vertices [ idx0 ][ 0 ]) * ( vertices [ idx1 ][ 1 ] + vertices [ idx0 ][ 1 ])
+235
+236 if order < 0 :
+237 ordered_edges_inbound_faces . append ( ordered_face )
+238 if order > 0 :
+239 ordered_edges_inbound_faces . append ([( e [ 1 ], e [ 0 ]) for e in reversed ( ordered_face )])
+240 if order == 0 :
+241 print ( " \n Error: no order detected for face " + str ( face ) + " \n " )
+242 exit ()
+243
+244 # Now we fill the tables with the info we have.
+245 useful_vertices_list = list ( useful_vertices_set )
+246 vertTable = np . zeros (( len ( useful_vertices_list ), 2 ))
+247 for i , idx in enumerate ( useful_vertices_list ):
+248 pos = vertices [ idx ]
+249 vertTable [ i ][ 0 ] = pos [ 0 ] # x pos vert
+250 vertTable [ i ][ 1 ] = pos [ 1 ] # y pos vert
+251
+252 faceTable = np . zeros (( len ( inbound_faces ), 1 ), dtype = np . int32 )
+253 for i , hedges_face in enumerate ( ordered_edges_inbound_faces ):
+254 for j , he in enumerate ( half_edges ):
+255 if he == hedges_face [ 0 ]:
+256 faceTable [ i ] = j # he_inside
+257 faceTable = _fate_selection ( faceTable , nb_fates , rng )
+258
+259 nb_half_edges = len ( half_edges )
+260 heTable = np . zeros (( nb_half_edges , 8 ), dtype = np . int32 )
+261 heTable [:, 4 ] = 1
+262 heTable [:, 6 ] = 1
+263 relevant_twins = []
+264 # HE TABLE :
+265 # 0 : previous half-edge.
+266 # 1 : next half-edge.
+267 # 2 : twin half-edge.
+268 # 3 : source vertex id + 2 if edge is inside, 0 if the edge is outside
+269 # 4 : target vertex id + 2 if edge is inside, 0 if the edge is outside
+270 # 5 : source vertex id + 2 if edge is outside, 0 if the edge is inside
+271 # 6 : target vertex id + 2 if edge is outside, 0 if the edge is inside
+272 # 7 : id of the face containing this half-edge.
+273 for i , he in enumerate ( half_edges ):
+274 belongs_to_any_face = False
+275 for hedges_face in ordered_edges_inbound_faces :
+276 if he in hedges_face :
+277 idx = hedges_face . index ( he )
+278 heTable [ i ][ 0 ] = half_edges . index ( hedges_face [( idx - 1 ) % len ( hedges_face )]) # he_prev
+279 heTable [ i ][ 1 ] = half_edges . index ( hedges_face [( idx + 1 ) % len ( hedges_face )]) # he_next
+280 # indices 0 and 1 are reserved for source or target vertices of "outside" edges.
+281 # So we have to add +2 to other indices.
+282 heTable [ i ][ 3 ] = useful_vertices_list . index ( he [ 0 ]) + 2 # vert source inner edges
+283 heTable [ i ][ 4 ] = useful_vertices_list . index ( he [ 1 ]) + 2 # vert target inner edges
+284 heTable [ i ][ 7 ] = ordered_edges_inbound_faces . index ( hedges_face ) # face
+285 belongs_to_any_face = True
+286 break
+287 twin_idx = half_edges . index (( he [ 1 ], he [ 0 ]))
+288 heTable [ i ][ 2 ] = twin_idx # he twin
+289 if not belongs_to_any_face :
+290 relevant_twins . append ( twin_idx )
+291
+292 # Angles are randomly chosen between 0 and pi/2 (with some margin to avoid extreme cases).
+293 angTable = np . ones ( nb_half_edges // 2 )
+294 for tidx in relevant_twins :
+295 angTable [ tidx // 2 ] = rng . random () * ( np . pi / 2 - 0.018 ) + 0.017
+296 heTable [ tidx ][ 5 ] = heTable [ tidx ][ 3 ] # vert source surface edges
+297 heTable [ tidx ][ 6 ] = heTable [ tidx ][ 4 ] # vert target surface edges
+298 heTable [ tidx ][ 3 ] = 0
+299 heTable [ tidx ][ 4 ] = 1
+300
+301 bounded_mesh = cls . _create ()
+302 bounded_mesh . vertices = jnp . array ( vertTable , dtype = np . float32 )
+303 bounded_mesh . angles = jnp . array ( angTable , dtype = np . float32 )
+304 bounded_mesh . faces = jnp . array ( faceTable , dtype = np . int32 )
+305 bounded_mesh . edges = jnp . array ( heTable , dtype = np . int32 )
+306 bounded_mesh . width = width
+307 bounded_mesh . height = height
+308
+309 return bounded_mesh
+310
+311 # If success was 1, we had not enough bounded faces, we add a new seed to see if it helps.
+312 # Otherwise we retry with new seeds entirely.
+313 seeds = (
+314 np . vstack ([ seeds , ( width , height ) * rng . random (( 1 , 2 ))])
+315 if success == 1
+316 else ( width , height ) * rng . random (( n_cells , 2 ))
+317 ) # type: ignore
+318
+319 @classmethod
+320 def create_empty ( cls ) -> Self :
+321 """Create an empty mesh. Use if you know what you're doing !"""
+322 return cls . _create ()
+323
+324 @classmethod
+325 def copy_mesh ( cls , other_mesh : Self ) -> Self :
+326 """Copy all parameters from another mesh in a new mesh."""
+327 mesh = cls . _create ()
+328 mesh . vertices = other_mesh . vertices . copy ()
+329 mesh . edges = other_mesh . edges . copy ()
+330 mesh . faces = other_mesh . faces . copy ()
+331 mesh . angles = other_mesh . angles . copy ()
+332 mesh . width = other_mesh . width
+333 mesh . height = other_mesh . height
+334 mesh . vertices_params = other_mesh . vertices_params . copy ()
+335 mesh . edges_params = other_mesh . edges_params . copy ()
+336 mesh . faces_params = other_mesh . faces_params . copy ()
+337
+338 return mesh
+339
+340 def save_mesh ( self , path : str ) -> None :
+341 """Save mesh to a file.
+342
+343 All BoundedMesh data is saved.
+344
+345 Args:
+346 path (str): Path to the saved file. The extension is .npz.
+347 """
+348 Path ( path ) . parent . mkdir ( parents = True , exist_ok = True )
+349 np . savez_compressed (
+350 path ,
+351 allow_pickle = False ,
+352 vertices = self . vertices ,
+353 edges = self . edges ,
+354 faces = self . faces ,
+355 angles = self . angles ,
+356 width = self . width ,
+357 height = self . height ,
+358 vertices_params = self . vertices_params ,
+359 edges_params = self . edges_params ,
+360 faces_params = self . faces_params ,
+361 )
+362
+363 @classmethod
+364 def load_mesh ( cls , path : str ) -> Self :
+365 """Load a mesh from a file.
+366
+367 All BoundedMesh data is reloaded.
+368
+369 Args:
+370 path (str): Path to the mesh file (.npz).
+371
+372 Returns:
+373 Mesh: the mesh loaded from the .npz file.
+374 """
+375 mesh_file = np . load ( path )
+376 mesh = cls . _create ()
+377 mesh . vertices , mesh . edges , mesh . faces , mesh . angles = (
+378 mesh_file [ "vertices" ],
+379 mesh_file [ "edges" ],
+380 mesh_file [ "faces" ],
+381 mesh_file [ "angles" ],
+382 )
+383 mesh . width = mesh_file [ "width" ]
+384 mesh . height = mesh_file [ "height" ]
+385 mesh . vertices_params = mesh_file [ "vertices_params" ]
+386 mesh . edges_params = mesh_file [ "edges_params" ]
+387 mesh . faces_params = mesh_file [ "faces_params" ]
+388 return mesh
+389
+390 def save_mesh_txt (
+391 self ,
+392 directory : str ,
+393 vertices_filename : str = "vertTable.txt" ,
+394 angles_filename : str = "angTable.txt" ,
+395 edges_filename : str = "heTable.txt" ,
+396 faces_filename : str = "faceTable.txt" ,
+397 vertices_params_filename : str = "vertParamsTable.txt" ,
+398 edges_params_filename : str = "heParamsTable.txt" ,
+399 faces_params_filename : str = "faceParamsTable.txt" ,
+400 constants_filename : str = "constants.txt" ,
+401 ) -> None :
+402 """Save a mesh in separate text files that can be read by numpy.
+403
+404 Only save the vertices, angles, edges and faces, not other parameters.
+405
+406 Args:
+407 directory (str): Path to the directory where to save the files.
+408 vertices_filename (str, optional): Filename for the vertices table. Defaults to "vertTable.txt".
+409 angles_filename (str, optional): Filename for the angles table. Defaults to "angTable.txt".
+410 edges_filename (str, optional): Filename for the half-edges table. Defaults to "heTable.txt".
+411 faces_filename (str, optional): Filename for the faces table. Defaults to "faceTable.txt".
+412 vertices_params_filename (str, optional): Filename for the vertices parameters table.
+413 Defaults to "vertParamsTable.txt".
+414 edges_params_filename (str, optional): Filename for the half-edges parameters table.
+415 Defaults to "heParamsTable.txt".
+416 faces_params_filename (str, optional): Filename for the faces parameters table.
+417 Defaults to "faceParamsTable.txt".
+418 constants_filename (str, optional): Filename for width/height.
+419 Defaults to "constants.txt".
+420 """
+421 dirpath = Path ( directory )
+422 dirpath . mkdir ( parents = True , exist_ok = True )
+423 np . savetxt ( dirpath / vertices_filename , self . vertices )
+424 np . savetxt ( dirpath / angles_filename , self . angles )
+425 np . savetxt ( dirpath / edges_filename , self . edges )
+426 np . savetxt ( dirpath / faces_filename , self . faces )
+427 np . savetxt ( dirpath / vertices_params_filename , self . vertices_params )
+428 np . savetxt ( dirpath / edges_params_filename , self . edges_params )
+429 np . savetxt ( dirpath / faces_params_filename , self . faces_params )
+430 with ( dirpath / constants_filename ) . open ( "w" ) as f :
+431 f . write ( f " { self . width } { self . height } " )
+432
+433 @classmethod
+434 def load_mesh_txt (
+435 cls ,
+436 directory : str ,
+437 vertices_filename : str = "vertTable.txt" ,
+438 angles_filename : str = "angTable.txt" ,
+439 edges_filename : str = "heTable.txt" ,
+440 faces_filename : str = "faceTable.txt" ,
+441 vertices_params_filename : str = "vertParamsTable.txt" ,
+442 edges_params_filename : str = "heParamsTable.txt" ,
+443 faces_params_filename : str = "faceParamsTable.txt" ,
+444 constants_filename : str = "constants.txt" ,
+445 ) -> Self :
+446 """Load a mesh from text files.
+447
+448 Only load the vertices, angles, edges and faces, not other parameters.
+449
+450 Args:
+451 directory (str): Directory where the text files are stored.
+452 vertices_filename (str, optional): Filename for the vertices table. Defaults to "vertTable.txt".
+453 angles_filename (str, optional): Filename for the angles table. Defaults to "angTable.txt".
+454 edges_filename (str, optional): Filename for the half-edges table. Defaults to "heTable.txt".
+455 faces_filename (str, optional): Filename for the faces table. Defaults to "faceTable.txt".
+456 vertices_params_filename (str, optional): Filename for the vertices parameters table.
+457 Defaults to "vertParamsTable.txt".
+458 edges_params_filename (str, optional): Filename for the half-edges parameters table.
+459 Defaults to "heParamsTable.txt".
+460 faces_params_filename (str, optional): Filename for the faces parameters table.
+461 Defaults to "faceParamsTable.txt".
+462 constants_filename (str, optional): Filename for width/height/MAX_EDGES_IN_ANY_FACE.
+463 Defaults to "constants.txt".
+464
+465 Returns:
+466 Self: The loaded mesh.
+467 """
+468 dirpath = Path ( directory )
+469 dirpath . mkdir ( parents = True , exist_ok = True )
+470
+471 mesh = cls . _create ()
+472 mesh . vertices = jnp . array ( np . loadtxt ( dirpath / vertices_filename , dtype = np . float64 ))
+473 mesh . angles = jnp . array ( np . loadtxt ( dirpath / angles_filename , dtype = np . float64 ))
+474 mesh . edges = jnp . array ( np . loadtxt ( dirpath / edges_filename , dtype = np . int64 ))
+475 mesh . faces = jnp . array ( np . loadtxt ( dirpath / faces_filename , dtype = np . int64 ))
+476 mesh . vertices_params = jnp . array ( np . loadtxt ( dirpath / vertices_params_filename , dtype = np . float64 ))
+477 mesh . edges_params = jnp . array ( np . loadtxt ( dirpath / edges_params_filename , dtype = np . int64 ))
+478 mesh . faces_params = jnp . array ( np . loadtxt ( dirpath / faces_params_filename , dtype = np . int64 ))
+479 with ( dirpath / constants_filename ) . open ( "r" ) as f :
+480 numbers = f . readline () . split ()
+481 mesh . width = float ( numbers [ 0 ])
+482 mesh . height = float ( numbers [ 1 ])
+483 return mesh
+484
+485 def get_length ( self , half_edge_id : Array ) -> Array :
+486 """Get the length of an edge."""
+487 vertTable = jnp . vstack ([ jnp . array ([[ 0.0 , 0.0 ], [ 1.0 , 1.0 ]]), self . vertices ])
+488 angTable = jnp . repeat ( self . angles , 2 )
+489
+490 def _get_length ( half_edge_id : Array ) -> Array :
+491 return get_any_length ( half_edge_id , vertTable , angTable , self . edges )
+492
+493 return jax . vmap ( _get_length )( half_edge_id )
+494
+495 def get_perimeter ( self , face_id : Array ) -> Array :
+496 """Get the area of a face."""
+497 vertTable = jnp . vstack ([ jnp . array ([[ 0.0 , 0.0 ], [ 1.0 , 1.0 ]]), self . vertices ])
+498 angTable = jnp . repeat ( self . angles , 2 )
+499
+500 def _get_perimeter ( face_id : Array ) -> Array :
+501 return get_perimeter_bounded ( face_id , vertTable , angTable , self . edges , self . faces )
+502
+503 return jax . vmap ( _get_perimeter )( face_id )
+504
+505 def get_area ( self , face_id : Array ) -> Array :
+506 """Get the area of a face."""
+507 vertTable = jnp . vstack ([ jnp . array ([[ 0.0 , 0.0 ], [ 1.0 , 1.0 ]]), self . vertices ])
+508 angTable = jnp . repeat ( self . angles , 2 )
+509
+510 def _get_area ( face_id : Array ) -> Array :
+511 return get_area_bounded ( face_id , vertTable , angTable , self . edges , self . faces )
+512
+513 return jax . vmap ( _get_area )( face_id )
+
+
+
+ Bounded mesh with arc circles for boundary cells.
+
+
For a BoundedMesh, vertices is a 2D array of floats of size (nb_vertices, 2) ;
+with the coordinates of the vertices (in ]0, width[ x ]0, height[ ).
+
+
edges is a 2D array of integers of size (nb_half_edges, 8), with:
+
+
+id of previous half-edge,
+id of next half-edge,
+id of twin half-edge,
+id of source vertex + 2 if current half-edge is an inside edge, else 0,
+id of target vertex + 2 if current half-edge is an inside edge, else 1,
+id of source vertex + 2 if current half-edge is an outside edge, else 0,
+id of target vertex + 2 if current half-edge is an outside edge, else 1,
+id of the face containing the half-edge.
+
+
+
faces is a 1D array of integers of size (nb_faces) containing the id of a half-edge belonging to this face.
+
+
angles is a 1D array of floats of size (nb_angles), with the angles sustaining the arcs of the free interfaces ;
+between 0 and PI / 2.
+
+
+
+
+
+
+
+ BoundedMesh ()
+
+ View Source
+
+
+
+
48 def __init__ ( self ) -> None :
+49 """Do not call the constructor."""
+50 super () . __init__ ()
+51 self . angles : Array = jnp . array ([])
+52 """Angle sustaining the arced free interfaces. Between 0 and PI / 2."""
+
+
+
+
Do not call the constructor.
+
+
+
+
+
+
+ angles : jax.Array
+
+
+
+
+
+
Angle sustaining the arced free interfaces. Between 0 and PI / 2.
+
+
+
+
+
+
+
+ nb_angles : int
+
+ View Source
+
+
+
+
54 @property
+55 def nb_angles ( self ) -> int :
+56 """Get the number of angles (free interfaces) of the mesh."""
+57 return len ( self . angles )
+
+
+
+
Get the number of angles (free interfaces) of the mesh.
+
+
+
+
+
+
+
+
@classmethod
+
+
def
+
from_random_seeds ( cls , nb_seeds : int , width : float , height : float , random_key : int , nb_fates : int = 2 ) -> Self :
+
+
View Source
+
+
+
+
59 @classmethod
+60 def from_random_seeds ( cls , nb_seeds : int , width : float , height : float , random_key : int , nb_fates : int = 2 ) -> Self :
+61 """Create a bounded Mesh from random seeds, based on a Voronoi diagram with arced free interfaces.
+62
+63 Args:
+64 nb_seeds (int): Number of random seeds to use.
+65 width (float): width of the box containing the seeds.
+66 height (float): height of the box containing the seeds.
+67 random_key (int): seed for a random number generator to add new seeds if needed, decide on cell fates...
+68 nb_fates (int, default=2): number of possible different fate marker for a cell.
+69
+70 Returns:
+71 Self: The corresponding mesh.
+72 """
+73 rng = np . random . default_rng ( seed = random_key )
+74 seeds = rng . random (( nb_seeds , 2 )) * ( width , height )
+75 return cls . from_seeds ( seeds , width , height , random_key , nb_fates )
+
+
+
+
Create a bounded Mesh from random seeds, based on a Voronoi diagram with arced free interfaces.
+
+
Arguments:
+
+
+nb_seeds (int): Number of random seeds to use.
+width (float): width of the box containing the seeds.
+height (float): height of the box containing the seeds.
+random_key (int): seed for a random number generator to add new seeds if needed, decide on cell fates...
+nb_fates (int, default=2): number of possible different fate marker for a cell.
+
+
+
Returns:
+
+
+ Self: The corresponding mesh.
+
+
+
+
+
+
+
+
+
@classmethod
+
+
def
+
from_seeds ( cls , seeds : numpy . ndarray [ typing . Any , numpy . dtype [ + _ScalarType_co ]] , width : float , height : float , random_key : int , nb_fates : int = 2 ) -> Self :
+
+
View Source
+
+
+
+
77 @classmethod
+ 78 def from_seeds ( cls , seeds : NDArray , width : float , height : float , random_key : int , nb_fates : int = 2 ) -> Self : # noqa: C901
+ 79 """Create a bounded Mesh from a list of given seeds.
+ 80
+ 81 The seeds are assumed to have x-coordinate in ]0, width[ and y-coordinate in ]0, height[.
+ 82 Note that the final mesh might not use your seeds if they don't work to create a correct
+ 83 bounded mesh via our method.
+ 84
+ 85 Args:
+ 86 seeds (Array[float32]): jax float array of seed positions of shape (nbSeeds, 2).
+ 87 width (float): width of the box containing the seeds.
+ 88 height (float): height of the box containing the seeds.
+ 89 random_key (int): seed for a random number generator to add new seeds if needed, decide on cell fates...
+ 90 nb_fates (int, default=2): number of possible different fate marker for a cell.
+ 91 """
+ 92 rng = np . random . default_rng ( seed = random_key )
+ 93 n_cells = len ( seeds ) # starting number of seeds must be equal to the desired number of cells (faces)
+ 94
+ 95 # We'll try to construct a Voronoi diagram with n_cells closed (bounded) cells.
+ 96 # This will not work with exactly n_cells seeds because there will be unbounded cells.
+ 97 # If the number of bounded cells is insufficient, we add a new seed to the list.
+ 98 # If there is too many bounded cells, we retry with entirely new seeds.
+ 99 # We also check that the bounded cells are connected.
+100 while True :
+101 success = 0 # if 1 : not enough bounded cells. If 2 : too many bounded cells.
+102
+103 # Create the Voronoi diagrams from current seed list.
+104 voronoi = Voronoi ( seeds )
+105 vertices = voronoi . vertices
+106 edges = voronoi . ridge_vertices
+107 faces = voronoi . regions # regions = faces = cells
+108
+109 # We count the number of bounded cells and the connectivity of vertices.
+110 inbound_faces = []
+111 inbound_vertices = np . zeros ( vertices . shape [ 0 ], dtype = np . int32 )
+112 for face in faces :
+113 if face and all ( item > - 1 for item in face ): # the face must not be an empty list
+114 face_vertices_positions = vertices [ face ]
+115 # We check that all of the face's vertices are in a box [0, width]x[0, height]
+116 if (
+117 np . all ( face_vertices_positions [:, 0 ] < width )
+118 and np . all ( face_vertices_positions [:, 1 ] < height )
+119 and np . all ( face_vertices_positions > 0 )
+120 ):
+121 inbound_faces . append ( face ) # the face is bounded
+122 inbound_vertices [ face ] += 1 # +1 to the connectivity of the face's vertices.
+123
+124 # getting rid of faces connected to a single other inbound face
+125 # (these can be problematic and lead to many special cases later on)
+126 while True :
+127 num_infaces = len ( inbound_faces )
+128 del_count = 0
+129 for i , face in enumerate ( reversed ( inbound_faces )):
+130 # only 2 (or less (not sure it's possible)) vertices of the face are shared with other faces :
+131 # that means it is connected to one other bounded face only -> We remove it.
+132 if np . sum ( inbound_vertices [ face ] > 1 ) <= 2 :
+133 inbound_vertices [ face ] -= 1
+134 del inbound_faces [ num_infaces - i - 1 ]
+135 del_count += 1
+136 # Removing one face can possibly alter other faces so we might do another loop.
+137 # We stop when there is no more face to remove.
+138 if del_count == 0 :
+139 break
+140
+141 # Check that we have the correct number of cells.
+142 if num_infaces < n_cells :
+143 success = 1
+144 elif num_infaces > n_cells :
+145 success = 2
+146 else :
+147 # There is exactly n_cells connected bounded faces.
+148 # Now, it is possible that a bounded face has vertices or edges
+149 # that are not shared with other faces. We get rid of those,
+150 # in order to have only one exterior edge that will be an arc circle.
+151 for i , face in enumerate ( inbound_faces ):
+152 useful_vertices = [] # List of the face vertices that are shared with other.
+153 # (We'll keep them and call them "useful").
+154 extra_edges = [] # List of edges to replace what we have removed
+155 last_useful = - 1 # ID of the last "useful" vertex. -1 at the beginning (we'll treat that case)
+156 new_edge = [] # New edge that will replace current vertices we're trying to remove
+157 incomplete_new_edge = False # State boolean : are we replacing vertices right now ?
+158 for vertex in face :
+159 if inbound_vertices [ vertex ] == 1 : # We found a vertex that is not shared with other faces.
+160 # We plan to remove it by not adding it to the useful vertices list,
+161 # and by creating a new edge from last useful vertex to the next one.
+162 if not incomplete_new_edge : # Detect if we're not already in the incomplete edge state
+163 new_edge = [] # re-init
+164 new_edge . append ( last_useful )
+165 incomplete_new_edge = True # Move to incomplete edge state.
+166 else : # We found a useful vertex
+167 useful_vertices . append ( vertex )
+168 last_useful = vertex
+169 if incomplete_new_edge : # If in incomplete edge state we can finally close the new edge.
+170 new_edge . append ( vertex )
+171 extra_edges . append ( new_edge )
+172 incomplete_new_edge = False
+173 # After looping through the vertices of the face, we need to take care of
+174 # two special cases : the first or the last vertex is not shared.
+175 if extra_edges and extra_edges [ 0 ][ 0 ] == - 1 :
+176 extra_edges [ 0 ][ 0 ] = useful_vertices [ - 1 ]
+177 elif incomplete_new_edge :
+178 new_edge . append ( useful_vertices [ 0 ])
+179 extra_edges . append ( new_edge )
+180 # The extra edges are added to the list of all edges
+181 edges . extend ( extra_edges )
+182 inbound_faces [ i ] = tuple (
+183 sorted ( useful_vertices )
+184 ) # And the face itself is replaced by only the useful vertices.
+185 # Note that the vertices here are not ordered in clockwise or counterclockwise order anymore.
+186 useful_vertices_set = set ( np . where ( inbound_vertices > 1 )[ 0 ]) # We filter the useful vertices.
+187
+188 # HALF EDGE DATA STRUCTURE
+189 # Filter edges with useful vertices only.
+190 useful_edges = [ tuple ( sorted ( e )) for e in edges if set ( e ) . issubset ( useful_vertices_set )]
+191
+192 # failing to abide by the following relation results in disconnected topologies
+193 if len ( useful_edges ) != ( n_cells - 1 ) * 3 :
+194 success = 2 # Case : we want to restart with new seeds because current solution is not OK.
+195 else :
+196 # We construct the half-edges.
+197 half_edges = []
+198 for e in useful_edges :
+199 half_edges . append ( e )
+200 # reciprocating edges
+201 half_edges . append (( e [ 1 ], e [ 0 ]))
+202
+203 # finding clockwise (or counterclockwise) half edge set for each face,
+204 # as we broke it earlier.
+205 ordered_edges_inbound_faces = []
+206 for face in inbound_faces :
+207 # Find all edges fon this face and we'll loow through them to order them.
+208 edges_face = [( f1 , f2 ) for f1 in face for f2 in face if ( f1 , f2 ) in useful_edges ]
+209
+210 i = 0
+211 start_edge = edges_face [ i ]
+212 ordered_face = [ start_edge ]
+213 e = start_edge
+214 visited = [ e ]
+215 while sorted ( edges_face ) != sorted ( visited ):
+216 if e [ 0 ] == start_edge [ 1 ] and e not in visited :
+217 ordered_face . append ( e )
+218 start_edge = e
+219 visited . append ( e )
+220 # We must be careful because some edges might be in the wrong order.
+221 if e [ 1 ] == start_edge [ 1 ] and e not in visited :
+222 ordered_face . append (( e [ 1 ], e [ 0 ]))
+223 start_edge = ( e [ 1 ], e [ 0 ])
+224 visited . append ( e )
+225 i += 1
+226 e = edges_face [ i % len ( edges_face )]
+227
+228 # Sanity check : do we have a correct ordering ?
+229 order = 0
+230 for e in ordered_face :
+231 idx0 = e [ 0 ]
+232 idx1 = e [ 1 ]
+233
+234 order += ( vertices [ idx1 ][ 0 ] - vertices [ idx0 ][ 0 ]) * ( vertices [ idx1 ][ 1 ] + vertices [ idx0 ][ 1 ])
+235
+236 if order < 0 :
+237 ordered_edges_inbound_faces . append ( ordered_face )
+238 if order > 0 :
+239 ordered_edges_inbound_faces . append ([( e [ 1 ], e [ 0 ]) for e in reversed ( ordered_face )])
+240 if order == 0 :
+241 print ( " \n Error: no order detected for face " + str ( face ) + " \n " )
+242 exit ()
+243
+244 # Now we fill the tables with the info we have.
+245 useful_vertices_list = list ( useful_vertices_set )
+246 vertTable = np . zeros (( len ( useful_vertices_list ), 2 ))
+247 for i , idx in enumerate ( useful_vertices_list ):
+248 pos = vertices [ idx ]
+249 vertTable [ i ][ 0 ] = pos [ 0 ] # x pos vert
+250 vertTable [ i ][ 1 ] = pos [ 1 ] # y pos vert
+251
+252 faceTable = np . zeros (( len ( inbound_faces ), 1 ), dtype = np . int32 )
+253 for i , hedges_face in enumerate ( ordered_edges_inbound_faces ):
+254 for j , he in enumerate ( half_edges ):
+255 if he == hedges_face [ 0 ]:
+256 faceTable [ i ] = j # he_inside
+257 faceTable = _fate_selection ( faceTable , nb_fates , rng )
+258
+259 nb_half_edges = len ( half_edges )
+260 heTable = np . zeros (( nb_half_edges , 8 ), dtype = np . int32 )
+261 heTable [:, 4 ] = 1
+262 heTable [:, 6 ] = 1
+263 relevant_twins = []
+264 # HE TABLE :
+265 # 0 : previous half-edge.
+266 # 1 : next half-edge.
+267 # 2 : twin half-edge.
+268 # 3 : source vertex id + 2 if edge is inside, 0 if the edge is outside
+269 # 4 : target vertex id + 2 if edge is inside, 0 if the edge is outside
+270 # 5 : source vertex id + 2 if edge is outside, 0 if the edge is inside
+271 # 6 : target vertex id + 2 if edge is outside, 0 if the edge is inside
+272 # 7 : id of the face containing this half-edge.
+273 for i , he in enumerate ( half_edges ):
+274 belongs_to_any_face = False
+275 for hedges_face in ordered_edges_inbound_faces :
+276 if he in hedges_face :
+277 idx = hedges_face . index ( he )
+278 heTable [ i ][ 0 ] = half_edges . index ( hedges_face [( idx - 1 ) % len ( hedges_face )]) # he_prev
+279 heTable [ i ][ 1 ] = half_edges . index ( hedges_face [( idx + 1 ) % len ( hedges_face )]) # he_next
+280 # indices 0 and 1 are reserved for source or target vertices of "outside" edges.
+281 # So we have to add +2 to other indices.
+282 heTable [ i ][ 3 ] = useful_vertices_list . index ( he [ 0 ]) + 2 # vert source inner edges
+283 heTable [ i ][ 4 ] = useful_vertices_list . index ( he [ 1 ]) + 2 # vert target inner edges
+284 heTable [ i ][ 7 ] = ordered_edges_inbound_faces . index ( hedges_face ) # face
+285 belongs_to_any_face = True
+286 break
+287 twin_idx = half_edges . index (( he [ 1 ], he [ 0 ]))
+288 heTable [ i ][ 2 ] = twin_idx # he twin
+289 if not belongs_to_any_face :
+290 relevant_twins . append ( twin_idx )
+291
+292 # Angles are randomly chosen between 0 and pi/2 (with some margin to avoid extreme cases).
+293 angTable = np . ones ( nb_half_edges // 2 )
+294 for tidx in relevant_twins :
+295 angTable [ tidx // 2 ] = rng . random () * ( np . pi / 2 - 0.018 ) + 0.017
+296 heTable [ tidx ][ 5 ] = heTable [ tidx ][ 3 ] # vert source surface edges
+297 heTable [ tidx ][ 6 ] = heTable [ tidx ][ 4 ] # vert target surface edges
+298 heTable [ tidx ][ 3 ] = 0
+299 heTable [ tidx ][ 4 ] = 1
+300
+301 bounded_mesh = cls . _create ()
+302 bounded_mesh . vertices = jnp . array ( vertTable , dtype = np . float32 )
+303 bounded_mesh . angles = jnp . array ( angTable , dtype = np . float32 )
+304 bounded_mesh . faces = jnp . array ( faceTable , dtype = np . int32 )
+305 bounded_mesh . edges = jnp . array ( heTable , dtype = np . int32 )
+306 bounded_mesh . width = width
+307 bounded_mesh . height = height
+308
+309 return bounded_mesh
+310
+311 # If success was 1, we had not enough bounded faces, we add a new seed to see if it helps.
+312 # Otherwise we retry with new seeds entirely.
+313 seeds = (
+314 np . vstack ([ seeds , ( width , height ) * rng . random (( 1 , 2 ))])
+315 if success == 1
+316 else ( width , height ) * rng . random (( n_cells , 2 ))
+317 ) # type: ignore
+
+
+
+
Create a bounded Mesh from a list of given seeds.
+
+
The seeds are assumed to have x-coordinate in ]0, width[ and y-coordinate in ]0, height[.
+Note that the final mesh might not use your seeds if they don't work to create a correct
+bounded mesh via our method.
+
+
Arguments:
+
+
+seeds (Array[float32]): jax float array of seed positions of shape (nbSeeds, 2).
+width (float): width of the box containing the seeds.
+height (float): height of the box containing the seeds.
+random_key (int): seed for a random number generator to add new seeds if needed, decide on cell fates...
+nb_fates (int, default=2): number of possible different fate marker for a cell.
+
+
+
+
+
+
+
+
+
@classmethod
+
+
def
+
create_empty (cls ) -> Self :
+
+
View Source
+
+
+
+
319 @classmethod
+320 def create_empty ( cls ) -> Self :
+321 """Create an empty mesh. Use if you know what you're doing !"""
+322 return cls . _create ()
+
+
+
+
Create an empty mesh. Use if you know what you're doing !
+
+
+
+
+
+
+
+
@classmethod
+
+
def
+
copy_mesh (cls , other_mesh : Self ) -> Self :
+
+
View Source
+
+
+
+
324 @classmethod
+325 def copy_mesh ( cls , other_mesh : Self ) -> Self :
+326 """Copy all parameters from another mesh in a new mesh."""
+327 mesh = cls . _create ()
+328 mesh . vertices = other_mesh . vertices . copy ()
+329 mesh . edges = other_mesh . edges . copy ()
+330 mesh . faces = other_mesh . faces . copy ()
+331 mesh . angles = other_mesh . angles . copy ()
+332 mesh . width = other_mesh . width
+333 mesh . height = other_mesh . height
+334 mesh . vertices_params = other_mesh . vertices_params . copy ()
+335 mesh . edges_params = other_mesh . edges_params . copy ()
+336 mesh . faces_params = other_mesh . faces_params . copy ()
+337
+338 return mesh
+
+
+
+
Copy all parameters from another mesh in a new mesh.
+
+
+
+
+
+
+
+
+ def
+ save_mesh (self , path : str ) -> None :
+
+ View Source
+
+
+
+
340 def save_mesh ( self , path : str ) -> None :
+341 """Save mesh to a file.
+342
+343 All BoundedMesh data is saved.
+344
+345 Args:
+346 path (str): Path to the saved file. The extension is .npz.
+347 """
+348 Path ( path ) . parent . mkdir ( parents = True , exist_ok = True )
+349 np . savez_compressed (
+350 path ,
+351 allow_pickle = False ,
+352 vertices = self . vertices ,
+353 edges = self . edges ,
+354 faces = self . faces ,
+355 angles = self . angles ,
+356 width = self . width ,
+357 height = self . height ,
+358 vertices_params = self . vertices_params ,
+359 edges_params = self . edges_params ,
+360 faces_params = self . faces_params ,
+361 )
+
+
+
+
Save mesh to a file.
+
+
All BoundedMesh data is saved.
+
+
Arguments:
+
+
+path (str): Path to the saved file. The extension is .npz.
+
+
+
+
+
+
+
+
+
@classmethod
+
+
def
+
load_mesh (cls , path : str ) -> Self :
+
+
View Source
+
+
+
+
363 @classmethod
+364 def load_mesh ( cls , path : str ) -> Self :
+365 """Load a mesh from a file.
+366
+367 All BoundedMesh data is reloaded.
+368
+369 Args:
+370 path (str): Path to the mesh file (.npz).
+371
+372 Returns:
+373 Mesh: the mesh loaded from the .npz file.
+374 """
+375 mesh_file = np . load ( path )
+376 mesh = cls . _create ()
+377 mesh . vertices , mesh . edges , mesh . faces , mesh . angles = (
+378 mesh_file [ "vertices" ],
+379 mesh_file [ "edges" ],
+380 mesh_file [ "faces" ],
+381 mesh_file [ "angles" ],
+382 )
+383 mesh . width = mesh_file [ "width" ]
+384 mesh . height = mesh_file [ "height" ]
+385 mesh . vertices_params = mesh_file [ "vertices_params" ]
+386 mesh . edges_params = mesh_file [ "edges_params" ]
+387 mesh . faces_params = mesh_file [ "faces_params" ]
+388 return mesh
+
+
+
+
Load a mesh from a file.
+
+
All BoundedMesh data is reloaded.
+
+
Arguments:
+
+
+path (str): Path to the mesh file (.npz).
+
+
+
Returns:
+
+
+ Mesh: the mesh loaded from the .npz file.
+
+
+
+
+
+
+
+
+
+ def
+ save_mesh_txt ( self , directory : str , vertices_filename : str = 'vertTable.txt' , angles_filename : str = 'angTable.txt' , edges_filename : str = 'heTable.txt' , faces_filename : str = 'faceTable.txt' , vertices_params_filename : str = 'vertParamsTable.txt' , edges_params_filename : str = 'heParamsTable.txt' , faces_params_filename : str = 'faceParamsTable.txt' , constants_filename : str = 'constants.txt' ) -> None :
+
+ View Source
+
+
+
+
390 def save_mesh_txt (
+391 self ,
+392 directory : str ,
+393 vertices_filename : str = "vertTable.txt" ,
+394 angles_filename : str = "angTable.txt" ,
+395 edges_filename : str = "heTable.txt" ,
+396 faces_filename : str = "faceTable.txt" ,
+397 vertices_params_filename : str = "vertParamsTable.txt" ,
+398 edges_params_filename : str = "heParamsTable.txt" ,
+399 faces_params_filename : str = "faceParamsTable.txt" ,
+400 constants_filename : str = "constants.txt" ,
+401 ) -> None :
+402 """Save a mesh in separate text files that can be read by numpy.
+403
+404 Only save the vertices, angles, edges and faces, not other parameters.
+405
+406 Args:
+407 directory (str): Path to the directory where to save the files.
+408 vertices_filename (str, optional): Filename for the vertices table. Defaults to "vertTable.txt".
+409 angles_filename (str, optional): Filename for the angles table. Defaults to "angTable.txt".
+410 edges_filename (str, optional): Filename for the half-edges table. Defaults to "heTable.txt".
+411 faces_filename (str, optional): Filename for the faces table. Defaults to "faceTable.txt".
+412 vertices_params_filename (str, optional): Filename for the vertices parameters table.
+413 Defaults to "vertParamsTable.txt".
+414 edges_params_filename (str, optional): Filename for the half-edges parameters table.
+415 Defaults to "heParamsTable.txt".
+416 faces_params_filename (str, optional): Filename for the faces parameters table.
+417 Defaults to "faceParamsTable.txt".
+418 constants_filename (str, optional): Filename for width/height.
+419 Defaults to "constants.txt".
+420 """
+421 dirpath = Path ( directory )
+422 dirpath . mkdir ( parents = True , exist_ok = True )
+423 np . savetxt ( dirpath / vertices_filename , self . vertices )
+424 np . savetxt ( dirpath / angles_filename , self . angles )
+425 np . savetxt ( dirpath / edges_filename , self . edges )
+426 np . savetxt ( dirpath / faces_filename , self . faces )
+427 np . savetxt ( dirpath / vertices_params_filename , self . vertices_params )
+428 np . savetxt ( dirpath / edges_params_filename , self . edges_params )
+429 np . savetxt ( dirpath / faces_params_filename , self . faces_params )
+430 with ( dirpath / constants_filename ) . open ( "w" ) as f :
+431 f . write ( f " { self . width } { self . height } " )
+
+
+
+
Save a mesh in separate text files that can be read by numpy.
+
+
Only save the vertices, angles, edges and faces, not other parameters.
+
+
Arguments:
+
+
+directory (str): Path to the directory where to save the files.
+vertices_filename (str, optional): Filename for the vertices table. Defaults to "vertTable.txt".
+angles_filename (str, optional): Filename for the angles table. Defaults to "angTable.txt".
+edges_filename (str, optional): Filename for the half-edges table. Defaults to "heTable.txt".
+faces_filename (str, optional): Filename for the faces table. Defaults to "faceTable.txt".
+vertices_params_filename (str, optional): Filename for the vertices parameters table.
+Defaults to "vertParamsTable.txt".
+edges_params_filename (str, optional): Filename for the half-edges parameters table.
+Defaults to "heParamsTable.txt".
+faces_params_filename (str, optional): Filename for the faces parameters table.
+Defaults to "faceParamsTable.txt".
+constants_filename (str, optional): Filename for width/height.
+Defaults to "constants.txt".
+
+
+
+
+
+
+
+
+
@classmethod
+
+
def
+
load_mesh_txt ( cls , directory : str , vertices_filename : str = 'vertTable.txt' , angles_filename : str = 'angTable.txt' , edges_filename : str = 'heTable.txt' , faces_filename : str = 'faceTable.txt' , vertices_params_filename : str = 'vertParamsTable.txt' , edges_params_filename : str = 'heParamsTable.txt' , faces_params_filename : str = 'faceParamsTable.txt' , constants_filename : str = 'constants.txt' ) -> Self :
+
+
View Source
+
+
+
+
433 @classmethod
+434 def load_mesh_txt (
+435 cls ,
+436 directory : str ,
+437 vertices_filename : str = "vertTable.txt" ,
+438 angles_filename : str = "angTable.txt" ,
+439 edges_filename : str = "heTable.txt" ,
+440 faces_filename : str = "faceTable.txt" ,
+441 vertices_params_filename : str = "vertParamsTable.txt" ,
+442 edges_params_filename : str = "heParamsTable.txt" ,
+443 faces_params_filename : str = "faceParamsTable.txt" ,
+444 constants_filename : str = "constants.txt" ,
+445 ) -> Self :
+446 """Load a mesh from text files.
+447
+448 Only load the vertices, angles, edges and faces, not other parameters.
+449
+450 Args:
+451 directory (str): Directory where the text files are stored.
+452 vertices_filename (str, optional): Filename for the vertices table. Defaults to "vertTable.txt".
+453 angles_filename (str, optional): Filename for the angles table. Defaults to "angTable.txt".
+454 edges_filename (str, optional): Filename for the half-edges table. Defaults to "heTable.txt".
+455 faces_filename (str, optional): Filename for the faces table. Defaults to "faceTable.txt".
+456 vertices_params_filename (str, optional): Filename for the vertices parameters table.
+457 Defaults to "vertParamsTable.txt".
+458 edges_params_filename (str, optional): Filename for the half-edges parameters table.
+459 Defaults to "heParamsTable.txt".
+460 faces_params_filename (str, optional): Filename for the faces parameters table.
+461 Defaults to "faceParamsTable.txt".
+462 constants_filename (str, optional): Filename for width/height/MAX_EDGES_IN_ANY_FACE.
+463 Defaults to "constants.txt".
+464
+465 Returns:
+466 Self: The loaded mesh.
+467 """
+468 dirpath = Path ( directory )
+469 dirpath . mkdir ( parents = True , exist_ok = True )
+470
+471 mesh = cls . _create ()
+472 mesh . vertices = jnp . array ( np . loadtxt ( dirpath / vertices_filename , dtype = np . float64 ))
+473 mesh . angles = jnp . array ( np . loadtxt ( dirpath / angles_filename , dtype = np . float64 ))
+474 mesh . edges = jnp . array ( np . loadtxt ( dirpath / edges_filename , dtype = np . int64 ))
+475 mesh . faces = jnp . array ( np . loadtxt ( dirpath / faces_filename , dtype = np . int64 ))
+476 mesh . vertices_params = jnp . array ( np . loadtxt ( dirpath / vertices_params_filename , dtype = np . float64 ))
+477 mesh . edges_params = jnp . array ( np . loadtxt ( dirpath / edges_params_filename , dtype = np . int64 ))
+478 mesh . faces_params = jnp . array ( np . loadtxt ( dirpath / faces_params_filename , dtype = np . int64 ))
+479 with ( dirpath / constants_filename ) . open ( "r" ) as f :
+480 numbers = f . readline () . split ()
+481 mesh . width = float ( numbers [ 0 ])
+482 mesh . height = float ( numbers [ 1 ])
+483 return mesh
+
+
+
+
Load a mesh from text files.
+
+
Only load the vertices, angles, edges and faces, not other parameters.
+
+
Arguments:
+
+
+directory (str): Directory where the text files are stored.
+vertices_filename (str, optional): Filename for the vertices table. Defaults to "vertTable.txt".
+angles_filename (str, optional): Filename for the angles table. Defaults to "angTable.txt".
+edges_filename (str, optional): Filename for the half-edges table. Defaults to "heTable.txt".
+faces_filename (str, optional): Filename for the faces table. Defaults to "faceTable.txt".
+vertices_params_filename (str, optional): Filename for the vertices parameters table.
+Defaults to "vertParamsTable.txt".
+edges_params_filename (str, optional): Filename for the half-edges parameters table.
+Defaults to "heParamsTable.txt".
+faces_params_filename (str, optional): Filename for the faces parameters table.
+Defaults to "faceParamsTable.txt".
+constants_filename (str, optional): Filename for width/height/MAX_EDGES_IN_ANY_FACE.
+Defaults to "constants.txt".
+
+
+
Returns:
+
+
+ Self: The loaded mesh.
+
+
+
+
+
+
+
+
+
+ def
+ get_length (self , half_edge_id : jax . Array ) -> jax . Array :
+
+ View Source
+
+
+
+
485 def get_length ( self , half_edge_id : Array ) -> Array :
+486 """Get the length of an edge."""
+487 vertTable = jnp . vstack ([ jnp . array ([[ 0.0 , 0.0 ], [ 1.0 , 1.0 ]]), self . vertices ])
+488 angTable = jnp . repeat ( self . angles , 2 )
+489
+490 def _get_length ( half_edge_id : Array ) -> Array :
+491 return get_any_length ( half_edge_id , vertTable , angTable , self . edges )
+492
+493 return jax . vmap ( _get_length )( half_edge_id )
+
+
+
+
Get the length of an edge.
+
+
+
+
+
+
+
+
+ def
+ get_perimeter (self , face_id : jax . Array ) -> jax . Array :
+
+ View Source
+
+
+
+
495 def get_perimeter ( self , face_id : Array ) -> Array :
+496 """Get the area of a face."""
+497 vertTable = jnp . vstack ([ jnp . array ([[ 0.0 , 0.0 ], [ 1.0 , 1.0 ]]), self . vertices ])
+498 angTable = jnp . repeat ( self . angles , 2 )
+499
+500 def _get_perimeter ( face_id : Array ) -> Array :
+501 return get_perimeter_bounded ( face_id , vertTable , angTable , self . edges , self . faces )
+502
+503 return jax . vmap ( _get_perimeter )( face_id )
+
+
+
+
Get the area of a face.
+
+
+
+
+
+
+
+
+ def
+ get_area (self , face_id : jax . Array ) -> jax . Array :
+
+ View Source
+
+
+
+
505 def get_area ( self , face_id : Array ) -> Array :
+506 """Get the area of a face."""
+507 vertTable = jnp . vstack ([ jnp . array ([[ 0.0 , 0.0 ], [ 1.0 , 1.0 ]]), self . vertices ])
+508 angTable = jnp . repeat ( self . angles , 2 )
+509
+510 def _get_area ( face_id : Array ) -> Array :
+511 return get_area_bounded ( face_id , vertTable , angTable , self . edges , self . faces )
+512
+513 return jax . vmap ( _get_area )( face_id )
+
+
+
+
Get the area of a face.
+
+
+
+
+
+
+
+
+
+ class
+ BilevelOptimizationMethod (enum.Enum ):
+
+ View Source
+
+
+
+ 7 class BilevelOptimizationMethod ( Enum ):
+ 8 """Which optimization method to use in the bi-level optimization."""
+ 9
+10 AUTOMATIC_DIFFERENTIATION = "ad"
+11 """Unrolls the inner optimization steps ; forward-mode JVP via `jax.jacfwd`,
+12 cost scales with the number of parameters and iterations."""
+13 EQUILIBRIUM_PROPAGATION = "ep"
+14 """Estimates the gradient from perturbed free vs nudged equilibria ; no backdrop required.
+15 Most efficient but depends on the perturbation size β."""
+16 IMPLICIT_DIFFERENTIATION = "id"
+17 """Differentiates the optimality condition ∇ₓE=0 via Implicit Function Theorem ; JVP variant ;
+18 requires Hessian solve and sensitive to ill-conditioning."""
+19 ADJOINT_STATE = "as"
+20 """Differentiates the optimality condition ∇ₓE=0 via Implicit Function Theorem ; VJP variant ;
+21 requires Hessian solve or sensitive to ill-conditioning."""
+
+
+
+ Which optimization method to use in the bi-level optimization.
+
+
+
+
+
+
+
+
Unrolls the inner optimization steps ; forward-mode JVP via jax.jacfwd,
+cost scales with the number of parameters and iterations.
+
+
+
+
+
+
+
+
+
Estimates the gradient from perturbed free vs nudged equilibria ; no backdrop required.
+Most efficient but depends on the perturbation size β.
+
+
+
+
+
+
+
+
+
Differentiates the optimality condition ∇ₓE=0 via Implicit Function Theorem ; JVP variant ;
+requires Hessian solve and sensitive to ill-conditioning.
+
+
+
+
+
+
+
+
+
Differentiates the optimality condition ∇ₓE=0 via Implicit Function Theorem ; VJP variant ;
+requires Hessian solve or sensitive to ill-conditioning.
+
+
+
+
+
+
+
+
+
+ class
+ _BilevelOptimizer :
+
+ View Source
+
+
+
+ 23 class _BilevelOptimizer :
+ 24 """Abstract base class for Bi-level optimizers.
+ 25
+ 26 Use the specialezed corresponding ones:
+ 27 - to optimize a `PbcMesh` use a `PbcBilevelOptimizer`,
+ 28 - to optimize a `BoundedMesh` use a `BoundedBilevelOptimizer`.
+ 29 """
+ 30
+ 31 def __init__ ( self ) -> None :
+ 32 """Initialize shared parameters and hyper-parameters between Bi-level optimizers."""
+ 33 self . bilevel_optimization_method : BilevelOptimizationMethod = BilevelOptimizationMethod . EQUILIBRIUM_PROPAGATION
+ 34 """Which bilevel optimization method to use. Defaults to Equilibrium Propagation."""
+ 35 self . inner_solver : optax . GradientTransformation = optax . sgd ( learning_rate = 0.01 )
+ 36 """Which inner solver to use. Defaults to `optax.sgd(learning_rate=0.01)`."""
+ 37 self . outer_solver : optax . GradientTransformation = optax . adam ( learning_rate = 0.0001 , nesterov = True )
+ 38 """Which outer solver to use. Defaults to `optax.adam(learning_rate=0.0001, nesterov=True)`"""
+ 39 self . loss_function_inner : Callable | None = None
+ 40 """Define your inner loss function."""
+ 41 self . loss_function_outer : Callable | None = None
+ 42 """Define your outer loss function."""
+ 43 self . custom_metrics : dict [ str , tuple [ Callable [[ Any , Any ], float ], list [ float ], list [ float ]]] = {}
+ 44 """Define custom metrics that can be saved during optimization.
+ 45 The function must take two arguments : a mesh and a bilevel optimizer ; and return a float."""
+ 46
+ 47 self . max_nb_iterations : int = 1000
+ 48 """Maximum number of iterations during an optimization step."""
+ 49 self . tolerance : float = 1e-4
+ 50 """Below this level, we consider that the loss is stagnating."""
+ 51 self . patience : int = 5
+ 52 """Maximum number of consecutive stagnating loss before we stop."""
+ 53
+ 54 self . min_dist_T1 : float = 0.005
+ 55 """Threshold to perform T1 transitions."""
+ 56 self . _update_T1 : bool = False
+ 57 self . _update_T1_func : Callable | None = None # value set by _set_update_T1_func
+ 58
+ 59 # These values will be set in the init function of child classes
+ 60 self . _inner_opt_func : Callable [[ Mesh , Array | None , Array | None , Array | None ], list [ float ]] | None = None
+ 61 self . _outer_opt_func : Callable [[ Mesh , Array | None , Array | None , Array | None ], None ] | None = None
+ 62
+ 63 self . update_T1 = True # Force the setting of update T1 func
+ 64 """Perform T1 transitions if necessary."""
+ 65
+ 66 # Targets
+ 67 self . vertices_target = jnp . array ([])
+ 68 """Vertices table of a target mesh if any."""
+ 69 self . edges_target = jnp . array ([])
+ 70 """Edges table of a target mesh if any."""
+ 71 self . faces_target = jnp . array ([])
+ 72 """Faces table of a target mesh if any."""
+ 73 # Those attributes are not always used (depends on the bilevel_optimization_method)
+ 74 self . image_target : Array = jnp . array ([])
+ 75 """Image target if any."""
+ 76 self . beta = 0.01
+ 77 """β parameter used for Equilibrium Propagation."""
+ 78
+ 79 def _set_update_T1_func ( self , b : bool ) -> None : # noqa: N802
+ 80 """Set the _update_T1_func callable with respect to whether it is needed or not."""
+ 81 raise NotImplementedError
+ 82
+ 83 @property
+ 84 def update_T1 ( self ) -> bool : # noqa: N802
+ 85 """Whether to process T1 topological operations or not."""
+ 86 return self . _update_T1
+ 87
+ 88 @update_T1 . setter
+ 89 def update_T1 ( self , value : bool ) -> None : # noqa: N802
+ 90 self . _update_T1 = value
+ 91 self . _set_update_T1_func ( value )
+ 92
+ 93 def compute_outer_loss (
+ 94 self ,
+ 95 mesh : Mesh ,
+ 96 only_on_vertices : None | list [ int ] = None ,
+ 97 only_on_edges : None | list [ int ] = None ,
+ 98 only_on_faces : None | list [ int ] = None ,
+ 99 ) -> float :
+100 """Get the result of self.loss_function_outer called with the correct arguments.
+101
+102 Must be implemented by child classes.
+103 """
+104 raise NotImplementedError
+105
+106 def compute_inner_loss (
+107 self ,
+108 mesh : Mesh ,
+109 only_on_vertices : None | list [ int ] = None ,
+110 only_on_edges : None | list [ int ] = None ,
+111 only_on_faces : None | list [ int ] = None ,
+112 ) -> float :
+113 """Get the result of self.loss_function_inner called with the correct arguments.
+114
+115 Must be implemented by child classes.
+116 """
+117 raise NotImplementedError
+118
+119 def inner_optimization (
+120 self ,
+121 mesh : Mesh ,
+122 only_on_vertices : None | list [ int ] = None ,
+123 only_on_edges : None | list [ int ] = None ,
+124 only_on_faces : None | list [ int ] = None ,
+125 ) -> list [ float ]:
+126 """Optimize the mesh for the inner loss function.
+127
+128 Args:
+129 mesh (Mesh): The mesh to act on.
+130 only_on_vertices (None | list[int] = None): Consider only a subset of vertices for the loss function.
+131 All vertices if None.
+132 only_on_edges (None | list[int] = None): Consider only a subset of edges for the loss function.
+133 All edges if None.
+134 only_on_faces (None | list[int] = None): Consider only a subset of faces for the loss function.
+135 All faces if None.
+136
+137 Returns:
+138 list[float]: Histor of loss values during optimization.
+139 """
+140 # To be defined by child classes
+141 if self . _inner_opt_func is None :
+142 msg = "The inner function was not initialized."
+143 raise AttributeError ( msg )
+144 else :
+145 return self . _inner_opt_func (
+146 mesh , * self . _selection_to_jax_arrays ( only_on_vertices , only_on_edges , only_on_faces )
+147 )
+148
+149 def outer_optimization (
+150 self ,
+151 mesh : Mesh ,
+152 only_on_vertices : None | list [ int ] = None ,
+153 only_on_edges : None | list [ int ] = None ,
+154 only_on_faces : None | list [ int ] = None ,
+155 ) -> None :
+156 """Optimize the mesh for the outer loss function.
+157
+158 Args:
+159 mesh (Mesh): The mesh to act on.
+160 only_on_vertices (None | list[int] = None): Consider only a subset of vertices for the loss function.
+161 All vertices if None.
+162 only_on_edges (None | list[int] = None): Consider only a subset of edges for the loss function.
+163 All edges if None.
+164 only_on_faces (None | list[int] = None): Consider only a subset of faces for the loss function.
+165 All faces if None.
+166 """
+167 # To be defined by child classes
+168 if self . _outer_opt_func is None :
+169 msg = "The outer function was not initialized."
+170 raise AttributeError ( msg )
+171 else :
+172 self . _outer_opt_func ( mesh , * self . _selection_to_jax_arrays ( only_on_vertices , only_on_edges , only_on_faces ))
+173
+174 def bilevel_optimization (
+175 self ,
+176 mesh : Mesh ,
+177 only_on_vertices : None | list [ int ] = None ,
+178 only_on_edges : None | list [ int ] = None ,
+179 only_on_faces : None | list [ int ] = None ,
+180 ) -> list [ float ]:
+181 """Optimize the mesh for the loss functions given.
+182
+183 Args:
+184 mesh (Mesh): The mesh to act on.
+185 only_on_vertices (None | list[int] = None): Consider only a subset of vertices for the loss function.
+186 All vertices if None.
+187 only_on_edges (None | list[int] = None): Consider only a subset of edges for the loss function.
+188 All edges if None.
+189 only_on_faces (None | list[int] = None): Consider only a subset of faces for the loss function.
+190 All faces if None.
+191
+192 Returns:
+193 list[float]: History of loss values during optimization.
+194 """
+195 self . outer_optimization ( mesh , only_on_vertices , only_on_edges , only_on_faces )
+196 return self . inner_optimization ( mesh , only_on_vertices , only_on_edges , only_on_faces )
+197
+198 def do_n_bilevel_optimization ( # noqa: C901
+199 self ,
+200 nb_epochs : int ,
+201 mesh : Mesh ,
+202 only_on_vertices : None | list [ int ] = None ,
+203 only_on_edges : None | list [ int ] = None ,
+204 only_on_faces : None | list [ int ] = None ,
+205 pre_inner_optimization : bool = False ,
+206 report_every : int = 0 ,
+207 also_report_to_stdout : bool = False ,
+208 save_plotmesh_every : int = 0 ,
+209 save_mesh_data_every : int = 0 ,
+210 save_folder : str = "." ,
+211 ) -> None :
+212 """Optimize the mesh for the loss function given.
+213
+214 Args:
+215 nb_epochs (int): The number of bilevel optimization steps to perform.
+216 mesh (Mesh): The mesh to act on.
+217 only_on_vertices (None | list[int] = None): Consider only a subset of vertices for the loss function.
+218 All vertices if None.
+219 only_on_edges (None | list[int] = None): Consider only a subset of edges for the loss function.
+220 All edges if None.
+221 only_on_faces (None | list[int] = None): Consider only a subset of faces for the loss function.
+222 All faces if None.
+223 pre_inner_optimization (bool = False): Whether to perform an optional initial inner optimization first.
+224 report_every (int = 0): If strictly positive, report current state (costs, metrics, time) at this frequency.
+225 also_report_to_stdout (bool = False): Whether to also report to stdout (print).
+226 save_plotmesh_every (int = 0): If positive, save the plot of the mesh at this frequency.
+227 save_mesh_data_every (int = 0): If positive, save the mesh data at this frequency.
+228 save_folder: (str = "."): Base folder where to save data if needed.
+229 """
+230 save_folder_path = Path ( save_folder )
+231 save_folder_path . mkdir ( parents = True , exist_ok = True )
+232 mesh_save_plot_folder = save_folder_path / "meshes_plot"
+233 mesh_save_data_folder = save_folder_path / "meshes_data"
+234 outer_cost_graph_filename = save_folder_path / "outer_cost_over_time.png"
+235 inner_cost_graph_filename = save_folder_path / "inner_cost_over_time.png"
+236 summary_filename = save_folder_path / "summary.csv"
+237 parameters_filename = save_folder_path / "hyper-parameters.txt"
+238
+239 with parameters_filename . open ( "w" ) as f :
+240 f . write ( self . self_summary ())
+241
+242 all_epoch_data = []
+243 epoch_data = []
+244 if save_plotmesh_every > 0 :
+245 mesh_save_plot_folder . mkdir ( parents = True , exist_ok = True )
+246 plot_mesh ( mesh , show = False , save = True , save_path = str ( mesh_save_plot_folder / "mesh_epoch_0.png" ))
+247 if save_mesh_data_every > 0 :
+248 mesh_save_data_folder . mkdir ( parents = True , exist_ok = True )
+249 mesh . save_mesh ( path = str ( mesh_save_data_folder / "mesh_epoch_0.npz" ))
+250
+251 x_epochs : list [ float ] = []
+252 y_outer_costs : list [ float ] = []
+253 y_inner_costs : list [ float ] = []
+254 if report_every > 0 :
+255 epoch_data = [ 0 ]
+256 outer_cost = self . compute_outer_loss ( mesh , only_on_vertices , only_on_edges , only_on_faces )
+257 inner_cost = self . compute_inner_loss ( mesh )
+258 x_epochs . append ( 0 )
+259 y_outer_costs . append ( outer_cost )
+260 y_inner_costs . append ( inner_cost )
+261 epoch_data . extend ([ outer_cost , inner_cost , 0 , 0 ]) # the zeros are time and delta time
+262 # Reset and put initial metrics
+263 for metric_name in self . custom_metrics :
+264 self . custom_metrics [ metric_name ][ 1 ] . clear ()
+265 self . custom_metrics [ metric_name ][ 2 ] . clear ()
+266 self . custom_metrics [ metric_name ][ 1 ] . append ( 0.0 )
+267 metric = self . custom_metrics [ metric_name ][ 0 ]( mesh , self )
+268 self . custom_metrics [ metric_name ][ 2 ] . append ( metric )
+269 epoch_data . append ( metric )
+270 all_epoch_data . append ( epoch_data )
+271 pandas . DataFrame (
+272 all_epoch_data ,
+273 columns = [
+274 "Epoch" ,
+275 "Outer cost" ,
+276 "Inner cost" ,
+277 "Time (s)" ,
+278 "Delta Time (s)" ,
+279 * ( metric_name for metric_name in self . custom_metrics ),
+280 ], # ty:ignore[invalid-argument-type]
+281 ) . to_csv ( summary_filename )
+282
+283 if also_report_to_stdout :
+284 msg = "First epoch may be a bit long."
+285 msg += f " Initial state: Outer cost = { outer_cost } , Inner cost = { inner_cost } "
+286 for metric_name in self . custom_metrics :
+287 msg += f ", { metric_name } = { self . custom_metrics [ metric_name ][ 2 ][ - 1 ] } "
+288 print ( msg )
+289
+290 if pre_inner_optimization :
+291 self . inner_optimization ( mesh , only_on_vertices , only_on_edges , only_on_faces )
+292
+293 t_initial = perf_counter ()
+294 for epoch in range ( 1 , nb_epochs + 1 ):
+295 epoch_data = [ epoch ]
+296 t_begin_of_epoch = perf_counter ()
+297
+298 self . bilevel_optimization ( mesh , only_on_vertices , only_on_edges , only_on_faces )
+299 t_end_of_epoch = perf_counter ()
+300 total_time = t_end_of_epoch - t_initial
+301 delta_time = t_end_of_epoch - t_begin_of_epoch
+302
+303 if save_plotmesh_every > 0 and epoch % save_plotmesh_every == 0 :
+304 plot_mesh ( mesh , show = False , save = True , save_path = str ( mesh_save_plot_folder / f "mesh_epoch_ { epoch } .png" ))
+305 if save_mesh_data_every > 0 and epoch % save_mesh_data_every == 0 :
+306 mesh . save_mesh ( path = str ( mesh_save_data_folder / f "mesh_epoch_ { epoch } .npz" ))
+307
+308 if report_every > 0 and epoch % report_every == 0 :
+309 # Compute results data for this step
+310 outer_cost = self . compute_outer_loss ( mesh , only_on_vertices , only_on_edges , only_on_faces )
+311 inner_cost = self . compute_inner_loss ( mesh )
+312 x_epochs . append ( epoch )
+313 y_outer_costs . append ( outer_cost )
+314 y_inner_costs . append ( inner_cost )
+315 epoch_data . extend ([ outer_cost , inner_cost , total_time , delta_time ]) # the zeros are time and delta time
+316 for metric_name in self . custom_metrics :
+317 self . custom_metrics [ metric_name ][ 1 ] . append ( epoch )
+318 metric = self . custom_metrics [ metric_name ][ 0 ]( mesh , self )
+319 self . custom_metrics [ metric_name ][ 2 ] . append ( metric )
+320 epoch_data . append ( metric )
+321
+322 all_epoch_data . append ( epoch_data )
+323 pandas . DataFrame (
+324 all_epoch_data ,
+325 columns = [
+326 "Epoch" ,
+327 "Outer cost" ,
+328 "Inner cost" ,
+329 "Time (s)" ,
+330 "Delta Time (s)" ,
+331 * ( metric_name for metric_name in self . custom_metrics ),
+332 ], # ty:ignore[invalid-argument-type]
+333 ) . to_csv ( summary_filename )
+334
+335 # Report in log.
+336 if also_report_to_stdout :
+337 msg = f " \n Epoch { epoch } / { nb_epochs } : Outer cost = { outer_cost } , Inner cost = { inner_cost } "
+338 for metric_name in self . custom_metrics :
+339 msg += f ", { metric_name } = { self . custom_metrics [ metric_name ][ 2 ][ - 1 ] } "
+340 msg += f ", Time = { total_time } s (+ { delta_time } s)"
+341 print ( msg )
+342
+343 # Update graphs
+344 save_simple_xy_graph (
+345 str ( outer_cost_graph_filename ),
+346 x_epochs ,
+347 y_outer_costs ,
+348 "Outer cost over time" ,
+349 "Epoch" ,
+350 "Outer cost" ,
+351 )
+352 save_simple_xy_graph (
+353 str ( inner_cost_graph_filename ),
+354 x_epochs ,
+355 y_inner_costs ,
+356 "Inner cost over time" ,
+357 "Epoch" ,
+358 "Inner cost" ,
+359 )
+360 for metric_name in self . custom_metrics :
+361 graph_name = save_folder_path / ( metric_name + ".png" )
+362 save_simple_xy_graph (
+363 str ( graph_name ),
+364 self . custom_metrics [ metric_name ][ 1 ],
+365 self . custom_metrics [ metric_name ][ 2 ],
+366 f " { metric_name } over time" ,
+367 "Epoch" ,
+368 metric_name ,
+369 )
+370
+371 def self_summary ( self ) -> str :
+372 """Give a summary of hyper-parameters of the optimizer."""
+373 summary = f "Bi-level optimization method: { self . bilevel_optimization_method . value } \n "
+374 if self . loss_function_inner is not None :
+375 summary += f "Inner loss function: { self . loss_function_inner . __name__ } \n " # ty:ignore[unresolved-attribute]
+376 else :
+377 summary += "Inner loss function: None \n "
+378 if self . loss_function_outer is not None :
+379 summary += f "Outer loss function: { self . loss_function_outer . __name__ } \n " # ty:ignore[unresolved-attribute]
+380 else :
+381 summary += "Outer loss function: None \n "
+382 summary += f "Max number of iterations: { self . max_nb_iterations } \n "
+383 summary += f "Tolerance: { self . tolerance } \n "
+384 summary += f "Patience: { self . patience } \n "
+385 summary += f "Minimum distance for T1: { self . min_dist_T1 } \n "
+386 summary += f "Update T1: { self . update_T1 } \n "
+387 if self . _update_T1_func is not None :
+388 summary += f "Update T1 function: { self . _update_T1_func . __name__ } \n " # ty:ignore[unresolved-attribute]
+389 else :
+390 summary += "Update T1 function: None \n "
+391 summary += f "Beta: { self . beta } "
+392 return summary
+393
+394 def add_custom_metric ( self , name : str , function : Callable [[ Any , Any ], float ]) -> None :
+395 """Add a custom metric to the metrics to save when performing n bilevel optimizations.
+396
+397 The function must take two arguments : a mesh and a bilevel optimizer.
+398 """
+399 self . custom_metrics [ name ] = ( function , [], [])
+400
+401 def remove_custom_metric ( self , name : str ) -> None :
+402 """Remove a custom metric from the metrics to save when performing n bilevel optimizations."""
+403 if name in self . custom_metrics :
+404 del self . custom_metrics [ name ]
+405
+406 def _selection_to_jax_arrays (
+407 self ,
+408 only_on_vertices : None | list [ int ] = None ,
+409 only_on_edges : None | list [ int ] = None ,
+410 only_on_faces : None | list [ int ] = None ,
+411 ) -> tuple [ Array | None , Array | None , Array | None ]:
+412 selected_vertices , selected_edges , selected_faces = None , None , None
+413 if only_on_vertices is not None :
+414 selected_vertices = jnp . array ( only_on_vertices )
+415 if only_on_edges is not None :
+416 selected_edges = jnp . array ( only_on_edges )
+417 if only_on_faces is not None :
+418 selected_faces = jnp . array ( only_on_faces )
+419 return selected_vertices , selected_edges , selected_faces
+
+
+
+ Abstract base class for Bi-level optimizers.
+
+
Use the specialezed corresponding ones:
+
+
+
+
+
+
+
+
+
+ _BilevelOptimizer ()
+
+ View Source
+
+
+
+
31 def __init__ ( self ) -> None :
+32 """Initialize shared parameters and hyper-parameters between Bi-level optimizers."""
+33 self . bilevel_optimization_method : BilevelOptimizationMethod = BilevelOptimizationMethod . EQUILIBRIUM_PROPAGATION
+34 """Which bilevel optimization method to use. Defaults to Equilibrium Propagation."""
+35 self . inner_solver : optax . GradientTransformation = optax . sgd ( learning_rate = 0.01 )
+36 """Which inner solver to use. Defaults to `optax.sgd(learning_rate=0.01)`."""
+37 self . outer_solver : optax . GradientTransformation = optax . adam ( learning_rate = 0.0001 , nesterov = True )
+38 """Which outer solver to use. Defaults to `optax.adam(learning_rate=0.0001, nesterov=True)`"""
+39 self . loss_function_inner : Callable | None = None
+40 """Define your inner loss function."""
+41 self . loss_function_outer : Callable | None = None
+42 """Define your outer loss function."""
+43 self . custom_metrics : dict [ str , tuple [ Callable [[ Any , Any ], float ], list [ float ], list [ float ]]] = {}
+44 """Define custom metrics that can be saved during optimization.
+45 The function must take two arguments : a mesh and a bilevel optimizer ; and return a float."""
+46
+47 self . max_nb_iterations : int = 1000
+48 """Maximum number of iterations during an optimization step."""
+49 self . tolerance : float = 1e-4
+50 """Below this level, we consider that the loss is stagnating."""
+51 self . patience : int = 5
+52 """Maximum number of consecutive stagnating loss before we stop."""
+53
+54 self . min_dist_T1 : float = 0.005
+55 """Threshold to perform T1 transitions."""
+56 self . _update_T1 : bool = False
+57 self . _update_T1_func : Callable | None = None # value set by _set_update_T1_func
+58
+59 # These values will be set in the init function of child classes
+60 self . _inner_opt_func : Callable [[ Mesh , Array | None , Array | None , Array | None ], list [ float ]] | None = None
+61 self . _outer_opt_func : Callable [[ Mesh , Array | None , Array | None , Array | None ], None ] | None = None
+62
+63 self . update_T1 = True # Force the setting of update T1 func
+64 """Perform T1 transitions if necessary."""
+65
+66 # Targets
+67 self . vertices_target = jnp . array ([])
+68 """Vertices table of a target mesh if any."""
+69 self . edges_target = jnp . array ([])
+70 """Edges table of a target mesh if any."""
+71 self . faces_target = jnp . array ([])
+72 """Faces table of a target mesh if any."""
+73 # Those attributes are not always used (depends on the bilevel_optimization_method)
+74 self . image_target : Array = jnp . array ([])
+75 """Image target if any."""
+76 self . beta = 0.01
+77 """β parameter used for Equilibrium Propagation."""
+
+
+
+
Initialize shared parameters and hyper-parameters between Bi-level optimizers.
+
+
+
+
+
+
+
+
+
Which bilevel optimization method to use. Defaults to Equilibrium Propagation.
+
+
+
+
+
+
+ inner_solver : optax._src.base.GradientTransformation
+
+
+
+
+
+
Which inner solver to use. Defaults to optax.sgd(learning_rate=0.01).
+
+
+
+
+
+
+ outer_solver : optax._src.base.GradientTransformation
+
+
+
+
+
+
Which outer solver to use. Defaults to optax.adam(learning_rate=0.0001, nesterov=True)
+
+
+
+
+
+
+ loss_function_inner : Callable | None
+
+
+
+
+
+
Define your inner loss function.
+
+
+
+
+
+
+ loss_function_outer : Callable | None
+
+
+
+
+
+
Define your outer loss function.
+
+
+
+
+
+
+ custom_metrics : dict[str, tuple[Callable[[typing.Any, typing.Any], float], list[float], list[float]]]
+
+
+
+
+
+
Define custom metrics that can be saved during optimization.
+The function must take two arguments : a mesh and a bilevel optimizer ; and return a float.
+
+
+
+
+
+
+ max_nb_iterations : int
+
+
+
+
+
+
Maximum number of iterations during an optimization step.
+
+
+
+
+
+
+ tolerance : float
+
+
+
+
+
+
Below this level, we consider that the loss is stagnating.
+
+
+
+
+
+
+ patience : int
+
+
+
+
+
+
Maximum number of consecutive stagnating loss before we stop.
+
+
+
+
+
+
+ min_dist_T1 : float
+
+
+
+
+
+
Threshold to perform T1 transitions.
+
+
+
+
+
+
+
+ update_T1 : bool
+
+ View Source
+
+
+
+
83 @property
+84 def update_T1 ( self ) -> bool : # noqa: N802
+85 """Whether to process T1 topological operations or not."""
+86 return self . _update_T1
+
+
+
+
Perform T1 transitions if necessary.
+
+
+
+
+
+
+ vertices_target
+
+
+
+
+
+
Vertices table of a target mesh if any.
+
+
+
+
+
+
+ edges_target
+
+
+
+
+
+
Edges table of a target mesh if any.
+
+
+
+
+
+
+ faces_target
+
+
+
+
+
+
Faces table of a target mesh if any.
+
+
+
+
+
+
+ image_target : jax.Array
+
+
+
+
+
+
+
+
+
+
+
+ beta
+
+
+
+
+
+
β parameter used for Equilibrium Propagation.
+
+
+
+
+
+
+
+
+
def
+
compute_outer_loss ( self , mesh : Mesh , only_on_vertices : None | list [ int ] = None , only_on_edges : None | list [ int ] = None , only_on_faces : None | list [ int ] = None ) -> float :
+
+
View Source
+
+
+
+
93 def compute_outer_loss (
+ 94 self ,
+ 95 mesh : Mesh ,
+ 96 only_on_vertices : None | list [ int ] = None ,
+ 97 only_on_edges : None | list [ int ] = None ,
+ 98 only_on_faces : None | list [ int ] = None ,
+ 99 ) -> float :
+100 """Get the result of self.loss_function_outer called with the correct arguments.
+101
+102 Must be implemented by child classes.
+103 """
+104 raise NotImplementedError
+
+
+
+
Get the result of self.loss_function_outer called with the correct arguments.
+
+
Must be implemented by child classes.
+
+
+
+
+
+
+
+
+
def
+
compute_inner_loss ( self , mesh : Mesh , only_on_vertices : None | list [ int ] = None , only_on_edges : None | list [ int ] = None , only_on_faces : None | list [ int ] = None ) -> float :
+
+
View Source
+
+
+
+
106 def compute_inner_loss (
+107 self ,
+108 mesh : Mesh ,
+109 only_on_vertices : None | list [ int ] = None ,
+110 only_on_edges : None | list [ int ] = None ,
+111 only_on_faces : None | list [ int ] = None ,
+112 ) -> float :
+113 """Get the result of self.loss_function_inner called with the correct arguments.
+114
+115 Must be implemented by child classes.
+116 """
+117 raise NotImplementedError
+
+
+
+
Get the result of self.loss_function_inner called with the correct arguments.
+
+
Must be implemented by child classes.
+
+
+
+
+
+
+
+
+
def
+
inner_optimization ( self , mesh : Mesh , only_on_vertices : None | list [ int ] = None , only_on_edges : None | list [ int ] = None , only_on_faces : None | list [ int ] = None ) -> list [ float ] :
+
+
View Source
+
+
+
+
119 def inner_optimization (
+120 self ,
+121 mesh : Mesh ,
+122 only_on_vertices : None | list [ int ] = None ,
+123 only_on_edges : None | list [ int ] = None ,
+124 only_on_faces : None | list [ int ] = None ,
+125 ) -> list [ float ]:
+126 """Optimize the mesh for the inner loss function.
+127
+128 Args:
+129 mesh (Mesh): The mesh to act on.
+130 only_on_vertices (None | list[int] = None): Consider only a subset of vertices for the loss function.
+131 All vertices if None.
+132 only_on_edges (None | list[int] = None): Consider only a subset of edges for the loss function.
+133 All edges if None.
+134 only_on_faces (None | list[int] = None): Consider only a subset of faces for the loss function.
+135 All faces if None.
+136
+137 Returns:
+138 list[float]: Histor of loss values during optimization.
+139 """
+140 # To be defined by child classes
+141 if self . _inner_opt_func is None :
+142 msg = "The inner function was not initialized."
+143 raise AttributeError ( msg )
+144 else :
+145 return self . _inner_opt_func (
+146 mesh , * self . _selection_to_jax_arrays ( only_on_vertices , only_on_edges , only_on_faces )
+147 )
+
+
+
+
Optimize the mesh for the inner loss function.
+
+
Arguments:
+
+
+mesh (Mesh): The mesh to act on.
+only_on_vertices (None | list[int] = None): Consider only a subset of vertices for the loss function.
+All vertices if None.
+only_on_edges (None | list[int] = None): Consider only a subset of edges for the loss function.
+All edges if None.
+only_on_faces (None | list[int] = None): Consider only a subset of faces for the loss function.
+All faces if None.
+
+
+
Returns:
+
+
+ list[float]: Histor of loss values during optimization.
+
+
+
+
+
+
+
+
+
+
def
+
outer_optimization ( self , mesh : Mesh , only_on_vertices : None | list [ int ] = None , only_on_edges : None | list [ int ] = None , only_on_faces : None | list [ int ] = None ) -> None :
+
+
View Source
+
+
+
+
149 def outer_optimization (
+150 self ,
+151 mesh : Mesh ,
+152 only_on_vertices : None | list [ int ] = None ,
+153 only_on_edges : None | list [ int ] = None ,
+154 only_on_faces : None | list [ int ] = None ,
+155 ) -> None :
+156 """Optimize the mesh for the outer loss function.
+157
+158 Args:
+159 mesh (Mesh): The mesh to act on.
+160 only_on_vertices (None | list[int] = None): Consider only a subset of vertices for the loss function.
+161 All vertices if None.
+162 only_on_edges (None | list[int] = None): Consider only a subset of edges for the loss function.
+163 All edges if None.
+164 only_on_faces (None | list[int] = None): Consider only a subset of faces for the loss function.
+165 All faces if None.
+166 """
+167 # To be defined by child classes
+168 if self . _outer_opt_func is None :
+169 msg = "The outer function was not initialized."
+170 raise AttributeError ( msg )
+171 else :
+172 self . _outer_opt_func ( mesh , * self . _selection_to_jax_arrays ( only_on_vertices , only_on_edges , only_on_faces ))
+
+
+
+
Optimize the mesh for the outer loss function.
+
+
Arguments:
+
+
+mesh (Mesh): The mesh to act on.
+only_on_vertices (None | list[int] = None): Consider only a subset of vertices for the loss function.
+All vertices if None.
+only_on_edges (None | list[int] = None): Consider only a subset of edges for the loss function.
+All edges if None.
+only_on_faces (None | list[int] = None): Consider only a subset of faces for the loss function.
+All faces if None.
+
+
+
+
+
+
+
+
+
+
def
+
bilevel_optimization ( self , mesh : Mesh , only_on_vertices : None | list [ int ] = None , only_on_edges : None | list [ int ] = None , only_on_faces : None | list [ int ] = None ) -> list [ float ] :
+
+
View Source
+
+
+
+
174 def bilevel_optimization (
+175 self ,
+176 mesh : Mesh ,
+177 only_on_vertices : None | list [ int ] = None ,
+178 only_on_edges : None | list [ int ] = None ,
+179 only_on_faces : None | list [ int ] = None ,
+180 ) -> list [ float ]:
+181 """Optimize the mesh for the loss functions given.
+182
+183 Args:
+184 mesh (Mesh): The mesh to act on.
+185 only_on_vertices (None | list[int] = None): Consider only a subset of vertices for the loss function.
+186 All vertices if None.
+187 only_on_edges (None | list[int] = None): Consider only a subset of edges for the loss function.
+188 All edges if None.
+189 only_on_faces (None | list[int] = None): Consider only a subset of faces for the loss function.
+190 All faces if None.
+191
+192 Returns:
+193 list[float]: History of loss values during optimization.
+194 """
+195 self . outer_optimization ( mesh , only_on_vertices , only_on_edges , only_on_faces )
+196 return self . inner_optimization ( mesh , only_on_vertices , only_on_edges , only_on_faces )
+
+
+
+
Optimize the mesh for the loss functions given.
+
+
Arguments:
+
+
+mesh (Mesh): The mesh to act on.
+only_on_vertices (None | list[int] = None): Consider only a subset of vertices for the loss function.
+All vertices if None.
+only_on_edges (None | list[int] = None): Consider only a subset of edges for the loss function.
+All edges if None.
+only_on_faces (None | list[int] = None): Consider only a subset of faces for the loss function.
+All faces if None.
+
+
+
Returns:
+
+
+ list[float]: History of loss values during optimization.
+
+
+
+
+
+
+
+
+
+
def
+
do_n_bilevel_optimization ( self , nb_epochs : int , mesh : Mesh , only_on_vertices : None | list [ int ] = None , only_on_edges : None | list [ int ] = None , only_on_faces : None | list [ int ] = None , pre_inner_optimization : bool = False , report_every : int = 0 , also_report_to_stdout : bool = False , save_plotmesh_every : int = 0 , save_mesh_data_every : int = 0 , save_folder : str = '.' ) -> None :
+
+
View Source
+
+
+
+
198 def do_n_bilevel_optimization ( # noqa: C901
+199 self ,
+200 nb_epochs : int ,
+201 mesh : Mesh ,
+202 only_on_vertices : None | list [ int ] = None ,
+203 only_on_edges : None | list [ int ] = None ,
+204 only_on_faces : None | list [ int ] = None ,
+205 pre_inner_optimization : bool = False ,
+206 report_every : int = 0 ,
+207 also_report_to_stdout : bool = False ,
+208 save_plotmesh_every : int = 0 ,
+209 save_mesh_data_every : int = 0 ,
+210 save_folder : str = "." ,
+211 ) -> None :
+212 """Optimize the mesh for the loss function given.
+213
+214 Args:
+215 nb_epochs (int): The number of bilevel optimization steps to perform.
+216 mesh (Mesh): The mesh to act on.
+217 only_on_vertices (None | list[int] = None): Consider only a subset of vertices for the loss function.
+218 All vertices if None.
+219 only_on_edges (None | list[int] = None): Consider only a subset of edges for the loss function.
+220 All edges if None.
+221 only_on_faces (None | list[int] = None): Consider only a subset of faces for the loss function.
+222 All faces if None.
+223 pre_inner_optimization (bool = False): Whether to perform an optional initial inner optimization first.
+224 report_every (int = 0): If strictly positive, report current state (costs, metrics, time) at this frequency.
+225 also_report_to_stdout (bool = False): Whether to also report to stdout (print).
+226 save_plotmesh_every (int = 0): If positive, save the plot of the mesh at this frequency.
+227 save_mesh_data_every (int = 0): If positive, save the mesh data at this frequency.
+228 save_folder: (str = "."): Base folder where to save data if needed.
+229 """
+230 save_folder_path = Path ( save_folder )
+231 save_folder_path . mkdir ( parents = True , exist_ok = True )
+232 mesh_save_plot_folder = save_folder_path / "meshes_plot"
+233 mesh_save_data_folder = save_folder_path / "meshes_data"
+234 outer_cost_graph_filename = save_folder_path / "outer_cost_over_time.png"
+235 inner_cost_graph_filename = save_folder_path / "inner_cost_over_time.png"
+236 summary_filename = save_folder_path / "summary.csv"
+237 parameters_filename = save_folder_path / "hyper-parameters.txt"
+238
+239 with parameters_filename . open ( "w" ) as f :
+240 f . write ( self . self_summary ())
+241
+242 all_epoch_data = []
+243 epoch_data = []
+244 if save_plotmesh_every > 0 :
+245 mesh_save_plot_folder . mkdir ( parents = True , exist_ok = True )
+246 plot_mesh ( mesh , show = False , save = True , save_path = str ( mesh_save_plot_folder / "mesh_epoch_0.png" ))
+247 if save_mesh_data_every > 0 :
+248 mesh_save_data_folder . mkdir ( parents = True , exist_ok = True )
+249 mesh . save_mesh ( path = str ( mesh_save_data_folder / "mesh_epoch_0.npz" ))
+250
+251 x_epochs : list [ float ] = []
+252 y_outer_costs : list [ float ] = []
+253 y_inner_costs : list [ float ] = []
+254 if report_every > 0 :
+255 epoch_data = [ 0 ]
+256 outer_cost = self . compute_outer_loss ( mesh , only_on_vertices , only_on_edges , only_on_faces )
+257 inner_cost = self . compute_inner_loss ( mesh )
+258 x_epochs . append ( 0 )
+259 y_outer_costs . append ( outer_cost )
+260 y_inner_costs . append ( inner_cost )
+261 epoch_data . extend ([ outer_cost , inner_cost , 0 , 0 ]) # the zeros are time and delta time
+262 # Reset and put initial metrics
+263 for metric_name in self . custom_metrics :
+264 self . custom_metrics [ metric_name ][ 1 ] . clear ()
+265 self . custom_metrics [ metric_name ][ 2 ] . clear ()
+266 self . custom_metrics [ metric_name ][ 1 ] . append ( 0.0 )
+267 metric = self . custom_metrics [ metric_name ][ 0 ]( mesh , self )
+268 self . custom_metrics [ metric_name ][ 2 ] . append ( metric )
+269 epoch_data . append ( metric )
+270 all_epoch_data . append ( epoch_data )
+271 pandas . DataFrame (
+272 all_epoch_data ,
+273 columns = [
+274 "Epoch" ,
+275 "Outer cost" ,
+276 "Inner cost" ,
+277 "Time (s)" ,
+278 "Delta Time (s)" ,
+279 * ( metric_name for metric_name in self . custom_metrics ),
+280 ], # ty:ignore[invalid-argument-type]
+281 ) . to_csv ( summary_filename )
+282
+283 if also_report_to_stdout :
+284 msg = "First epoch may be a bit long."
+285 msg += f " Initial state: Outer cost = { outer_cost } , Inner cost = { inner_cost } "
+286 for metric_name in self . custom_metrics :
+287 msg += f ", { metric_name } = { self . custom_metrics [ metric_name ][ 2 ][ - 1 ] } "
+288 print ( msg )
+289
+290 if pre_inner_optimization :
+291 self . inner_optimization ( mesh , only_on_vertices , only_on_edges , only_on_faces )
+292
+293 t_initial = perf_counter ()
+294 for epoch in range ( 1 , nb_epochs + 1 ):
+295 epoch_data = [ epoch ]
+296 t_begin_of_epoch = perf_counter ()
+297
+298 self . bilevel_optimization ( mesh , only_on_vertices , only_on_edges , only_on_faces )
+299 t_end_of_epoch = perf_counter ()
+300 total_time = t_end_of_epoch - t_initial
+301 delta_time = t_end_of_epoch - t_begin_of_epoch
+302
+303 if save_plotmesh_every > 0 and epoch % save_plotmesh_every == 0 :
+304 plot_mesh ( mesh , show = False , save = True , save_path = str ( mesh_save_plot_folder / f "mesh_epoch_ { epoch } .png" ))
+305 if save_mesh_data_every > 0 and epoch % save_mesh_data_every == 0 :
+306 mesh . save_mesh ( path = str ( mesh_save_data_folder / f "mesh_epoch_ { epoch } .npz" ))
+307
+308 if report_every > 0 and epoch % report_every == 0 :
+309 # Compute results data for this step
+310 outer_cost = self . compute_outer_loss ( mesh , only_on_vertices , only_on_edges , only_on_faces )
+311 inner_cost = self . compute_inner_loss ( mesh )
+312 x_epochs . append ( epoch )
+313 y_outer_costs . append ( outer_cost )
+314 y_inner_costs . append ( inner_cost )
+315 epoch_data . extend ([ outer_cost , inner_cost , total_time , delta_time ]) # the zeros are time and delta time
+316 for metric_name in self . custom_metrics :
+317 self . custom_metrics [ metric_name ][ 1 ] . append ( epoch )
+318 metric = self . custom_metrics [ metric_name ][ 0 ]( mesh , self )
+319 self . custom_metrics [ metric_name ][ 2 ] . append ( metric )
+320 epoch_data . append ( metric )
+321
+322 all_epoch_data . append ( epoch_data )
+323 pandas . DataFrame (
+324 all_epoch_data ,
+325 columns = [
+326 "Epoch" ,
+327 "Outer cost" ,
+328 "Inner cost" ,
+329 "Time (s)" ,
+330 "Delta Time (s)" ,
+331 * ( metric_name for metric_name in self . custom_metrics ),
+332 ], # ty:ignore[invalid-argument-type]
+333 ) . to_csv ( summary_filename )
+334
+335 # Report in log.
+336 if also_report_to_stdout :
+337 msg = f " \n Epoch { epoch } / { nb_epochs } : Outer cost = { outer_cost } , Inner cost = { inner_cost } "
+338 for metric_name in self . custom_metrics :
+339 msg += f ", { metric_name } = { self . custom_metrics [ metric_name ][ 2 ][ - 1 ] } "
+340 msg += f ", Time = { total_time } s (+ { delta_time } s)"
+341 print ( msg )
+342
+343 # Update graphs
+344 save_simple_xy_graph (
+345 str ( outer_cost_graph_filename ),
+346 x_epochs ,
+347 y_outer_costs ,
+348 "Outer cost over time" ,
+349 "Epoch" ,
+350 "Outer cost" ,
+351 )
+352 save_simple_xy_graph (
+353 str ( inner_cost_graph_filename ),
+354 x_epochs ,
+355 y_inner_costs ,
+356 "Inner cost over time" ,
+357 "Epoch" ,
+358 "Inner cost" ,
+359 )
+360 for metric_name in self . custom_metrics :
+361 graph_name = save_folder_path / ( metric_name + ".png" )
+362 save_simple_xy_graph (
+363 str ( graph_name ),
+364 self . custom_metrics [ metric_name ][ 1 ],
+365 self . custom_metrics [ metric_name ][ 2 ],
+366 f " { metric_name } over time" ,
+367 "Epoch" ,
+368 metric_name ,
+369 )
+
+
+
+
Optimize the mesh for the loss function given.
+
+
Arguments:
+
+
+nb_epochs (int): The number of bilevel optimization steps to perform.
+mesh (Mesh): The mesh to act on.
+only_on_vertices (None | list[int] = None): Consider only a subset of vertices for the loss function.
+All vertices if None.
+only_on_edges (None | list[int] = None): Consider only a subset of edges for the loss function.
+All edges if None.
+only_on_faces (None | list[int] = None): Consider only a subset of faces for the loss function.
+All faces if None.
+pre_inner_optimization (bool = False): Whether to perform an optional initial inner optimization first.
+report_every (int = 0): If strictly positive, report current state (costs, metrics, time) at this frequency.
+also_report_to_stdout (bool = False): Whether to also report to stdout (print).
+save_plotmesh_every (int = 0): If positive, save the plot of the mesh at this frequency.
+save_mesh_data_every (int = 0): If positive, save the mesh data at this frequency.
+save_folder: (str = "."): Base folder where to save data if needed.
+
+
+
+
+
+
+
+
+
+ def
+ self_summary (self ) -> str :
+
+ View Source
+
+
+
+
371 def self_summary ( self ) -> str :
+372 """Give a summary of hyper-parameters of the optimizer."""
+373 summary = f "Bi-level optimization method: { self . bilevel_optimization_method . value } \n "
+374 if self . loss_function_inner is not None :
+375 summary += f "Inner loss function: { self . loss_function_inner . __name__ } \n " # ty:ignore[unresolved-attribute]
+376 else :
+377 summary += "Inner loss function: None \n "
+378 if self . loss_function_outer is not None :
+379 summary += f "Outer loss function: { self . loss_function_outer . __name__ } \n " # ty:ignore[unresolved-attribute]
+380 else :
+381 summary += "Outer loss function: None \n "
+382 summary += f "Max number of iterations: { self . max_nb_iterations } \n "
+383 summary += f "Tolerance: { self . tolerance } \n "
+384 summary += f "Patience: { self . patience } \n "
+385 summary += f "Minimum distance for T1: { self . min_dist_T1 } \n "
+386 summary += f "Update T1: { self . update_T1 } \n "
+387 if self . _update_T1_func is not None :
+388 summary += f "Update T1 function: { self . _update_T1_func . __name__ } \n " # ty:ignore[unresolved-attribute]
+389 else :
+390 summary += "Update T1 function: None \n "
+391 summary += f "Beta: { self . beta } "
+392 return summary
+
+
+
+
Give a summary of hyper-parameters of the optimizer.
+
+
+
+
+
+
+
+
+ def
+ add_custom_metric ( self , name : str , function : Callable [[ typing . Any , typing . Any ], float ] ) -> None :
+
+ View Source
+
+
+
+
394 def add_custom_metric ( self , name : str , function : Callable [[ Any , Any ], float ]) -> None :
+395 """Add a custom metric to the metrics to save when performing n bilevel optimizations.
+396
+397 The function must take two arguments : a mesh and a bilevel optimizer.
+398 """
+399 self . custom_metrics [ name ] = ( function , [], [])
+
+
+
+
Add a custom metric to the metrics to save when performing n bilevel optimizations.
+
+
The function must take two arguments : a mesh and a bilevel optimizer.
+
+
+
+
+
+
+
+
+ def
+ remove_custom_metric (self , name : str ) -> None :
+
+ View Source
+
+
+
+
401 def remove_custom_metric ( self , name : str ) -> None :
+402 """Remove a custom metric from the metrics to save when performing n bilevel optimizations."""
+403 if name in self . custom_metrics :
+404 del self . custom_metrics [ name ]
+
+
+
+
Remove a custom metric from the metrics to save when performing n bilevel optimizations.
+
+
+
+
+
+
+
+
+
+ 22 class PbcBilevelOptimizer ( _BilevelOptimizer ):
+ 23 """Bi-level optimizer for periodic boundary condition meshes (`PbcMesh`)."""
+ 24
+ 25 def __init__ ( self ) -> None :
+ 26 """Create a Bi-level optimizer for periodic boundary condition meshes with default parameters.
+ 27
+ 28 Does not specify the inner and outer loss yet.
+ 29 """
+ 30 super () . __init__ ()
+ 31 self . _inner_opt_func = self . _inner_opt
+ 32 self . _outer_opt_func = self . _outer_opt
+ 33
+ 34 def _set_update_T1_func ( self , b : bool ) -> None : # noqa: N802
+ 35 """Set the _update_T1_func callable with respect to whether it is needed or not.
+ 36
+ 37 Must be implemented by child classes.
+ 38 """
+ 39 if b :
+ 40 self . _update_T1_func = update_T1
+ 41 else :
+ 42 self . _update_T1_func = do_not_update_T1
+ 43
+ 44 def compute_outer_loss (
+ 45 self ,
+ 46 mesh : Mesh ,
+ 47 only_on_vertices : None | list [ int ] = None ,
+ 48 only_on_edges : None | list [ int ] = None ,
+ 49 only_on_faces : None | list [ int ] = None ,
+ 50 ) -> float :
+ 51 """Get the result of self.loss_function_outer called with the correct arguments."""
+ 52 selected_vertices , selected_edges , selected_faces = self . _selection_to_jax_arrays (
+ 53 only_on_vertices , only_on_edges , only_on_faces
+ 54 )
+ 55 if not isinstance ( mesh , PbcMesh ):
+ 56 msg = "The mesh given to a PbcBilevelOptimizer must be a PbcMesh."
+ 57 raise ValueError ( msg )
+ 58 elif self . loss_function_outer is None :
+ 59 msg = "The outer loss function was not defined."
+ 60 raise AttributeError ( msg )
+ 61 return float (
+ 62 self . loss_function_outer (
+ 63 mesh . vertices ,
+ 64 mesh . edges ,
+ 65 mesh . faces ,
+ 66 mesh . width ,
+ 67 mesh . height ,
+ 68 self . vertices_target ,
+ 69 self . edges_target ,
+ 70 self . faces_target ,
+ 71 selected_vertices ,
+ 72 selected_edges ,
+ 73 selected_faces ,
+ 74 self . image_target ,
+ 75 )
+ 76 )
+ 77
+ 78 def compute_inner_loss (
+ 79 self ,
+ 80 mesh : Mesh ,
+ 81 only_on_vertices : None | list [ int ] = None , # noqa: ARG002
+ 82 only_on_edges : None | list [ int ] = None , # noqa: ARG002
+ 83 only_on_faces : None | list [ int ] = None , # noqa: ARG002
+ 84 ) -> float :
+ 85 """Get the result of self.loss_function_inner called with the correct arguments."""
+ 86 if not isinstance ( mesh , PbcMesh ):
+ 87 msg = "The mesh given to a PbcBilevelOptimizer must be a PbcMesh."
+ 88 raise ValueError ( msg )
+ 89 elif self . loss_function_inner is None :
+ 90 msg = "The inner loss function was not defined."
+ 91 raise AttributeError ( msg )
+ 92 return float (
+ 93 self . loss_function_inner (
+ 94 mesh . vertices ,
+ 95 mesh . edges ,
+ 96 mesh . faces ,
+ 97 mesh . vertices_params ,
+ 98 mesh . edges_params ,
+ 99 mesh . faces_params ,
+100 )
+101 )
+102
+103 def _inner_opt (
+104 self ,
+105 mesh : Mesh ,
+106 only_on_vertices : None | Array = None ,
+107 only_on_edges : None | Array = None ,
+108 only_on_faces : None | Array = None ,
+109 ) -> list :
+110 """Call the correct inner optimization function for a PbcMesh."""
+111 if not isinstance ( mesh , PbcMesh ):
+112 msg = "The mesh given to a PbcBilevelOptimizer must be a PbcMesh."
+113 raise ValueError ( msg )
+114 elif self . loss_function_inner is None :
+115 msg = "The inner loss function was not defined."
+116 raise AttributeError ( msg )
+117 elif self . _update_T1_func is None :
+118 msg = "The update T1 method was not set by a boolean."
+119 raise AttributeError ( msg )
+120 else :
+121 ( mesh . vertices , mesh . edges , mesh . faces ), loss_history = inner_opt (
+122 vertTable = mesh . vertices ,
+123 heTable = mesh . edges ,
+124 faceTable = mesh . faces ,
+125 width = mesh . width ,
+126 height = mesh . height ,
+127 vert_params = mesh . vertices_params ,
+128 he_params = mesh . edges_params ,
+129 face_params = mesh . faces_params ,
+130 L_in = self . loss_function_inner ,
+131 solver = self . inner_solver ,
+132 min_dist_T1 = self . min_dist_T1 ,
+133 iterations_max = self . max_nb_iterations ,
+134 tolerance = self . tolerance ,
+135 patience = self . patience ,
+136 selected_verts = only_on_vertices ,
+137 selected_hes = only_on_edges ,
+138 selected_faces = only_on_faces ,
+139 update_t1_func = self . _update_T1_func ,
+140 )
+141 return list ( loss_history )
+142
+143 def _outer_opt (
+144 self ,
+145 mesh : Mesh ,
+146 selected_vertices : None | Array = None ,
+147 selected_edges : None | Array = None ,
+148 selected_faces : None | Array = None ,
+149 ) -> None :
+150 """Call the correct outer optimization function for a PbcMesh."""
+151 if not isinstance ( mesh , PbcMesh ):
+152 msg = "The mesh given to a PbcBilevelOptimizer must be a PbcMesh."
+153 raise ValueError ( msg )
+154 elif self . loss_function_inner is None :
+155 msg = "The inner loss function was not defined."
+156 raise AttributeError ( msg )
+157 elif self . loss_function_outer is None :
+158 msg = "The outer loss function was not defined."
+159 raise AttributeError ( msg )
+160 elif self . _update_T1_func is None :
+161 msg = "The update T1 method was not set by a boolean."
+162 raise AttributeError ( msg )
+163 else :
+164 match self . bilevel_optimization_method :
+165 case BilevelOptimizationMethod . AUTOMATIC_DIFFERENTIATION :
+166 mesh . vertices_params , mesh . edges_params , mesh . faces_params = outer_opt (
+167 mesh . vertices ,
+168 mesh . edges ,
+169 mesh . faces ,
+170 mesh . width ,
+171 mesh . height ,
+172 mesh . vertices_params ,
+173 mesh . edges_params ,
+174 mesh . faces_params ,
+175 self . vertices_target ,
+176 self . edges_target ,
+177 self . faces_target ,
+178 self . loss_function_inner ,
+179 self . loss_function_outer ,
+180 self . inner_solver ,
+181 self . outer_solver ,
+182 self . min_dist_T1 ,
+183 self . max_nb_iterations ,
+184 self . tolerance ,
+185 self . patience ,
+186 selected_vertices ,
+187 selected_edges ,
+188 selected_faces ,
+189 self . image_target ,
+190 self . _update_T1_func ,
+191 )
+192
+193 case BilevelOptimizationMethod . EQUILIBRIUM_PROPAGATION :
+194 mesh . vertices_params , mesh . edges_params , mesh . faces_params = outer_eq_prop (
+195 mesh . vertices ,
+196 mesh . edges ,
+197 mesh . faces ,
+198 mesh . width ,
+199 mesh . height ,
+200 mesh . vertices_params ,
+201 mesh . edges_params ,
+202 mesh . faces_params ,
+203 self . vertices_target ,
+204 self . edges_target ,
+205 self . faces_target ,
+206 self . loss_function_inner ,
+207 self . loss_function_outer ,
+208 self . inner_solver ,
+209 self . outer_solver ,
+210 self . min_dist_T1 ,
+211 self . max_nb_iterations ,
+212 self . tolerance ,
+213 self . patience ,
+214 selected_vertices ,
+215 selected_edges ,
+216 selected_faces ,
+217 self . image_target ,
+218 self . beta ,
+219 self . _update_T1_func ,
+220 )
+221
+222 case BilevelOptimizationMethod . IMPLICIT_DIFFERENTIATION :
+223 mesh . vertices_params , mesh . edges_params , mesh . faces_params = outer_implicit (
+224 mesh . vertices ,
+225 mesh . edges ,
+226 mesh . faces ,
+227 mesh . width ,
+228 mesh . height ,
+229 mesh . vertices_params ,
+230 mesh . edges_params ,
+231 mesh . faces_params ,
+232 self . vertices_target ,
+233 self . edges_target ,
+234 self . faces_target ,
+235 self . loss_function_inner ,
+236 self . loss_function_outer ,
+237 self . inner_solver ,
+238 self . outer_solver ,
+239 self . min_dist_T1 ,
+240 self . max_nb_iterations ,
+241 self . tolerance ,
+242 self . patience ,
+243 selected_vertices ,
+244 selected_edges ,
+245 selected_faces ,
+246 self . image_target ,
+247 self . _update_T1_func ,
+248 )
+249
+250 case BilevelOptimizationMethod . ADJOINT_STATE :
+251 mesh . vertices_params , mesh . edges_params , mesh . faces_params = outer_adjoint_state (
+252 mesh . vertices ,
+253 mesh . edges ,
+254 mesh . faces ,
+255 mesh . width ,
+256 mesh . height ,
+257 mesh . vertices_params ,
+258 mesh . edges_params ,
+259 mesh . faces_params ,
+260 self . vertices_target ,
+261 self . edges_target ,
+262 self . faces_target ,
+263 self . loss_function_inner ,
+264 self . loss_function_outer ,
+265 self . inner_solver ,
+266 self . outer_solver ,
+267 self . min_dist_T1 ,
+268 self . max_nb_iterations ,
+269 self . tolerance ,
+270 self . patience ,
+271 selected_vertices ,
+272 selected_edges ,
+273 selected_faces ,
+274 self . image_target ,
+275 self . _update_T1_func ,
+276 )
+277
+278 case _ :
+279 msg = f "Method not recognized. Must be a BilevelOptimizationMethod. \
+280 Got { self . bilevel_optimization_method } ."
+281 raise ValueError ( msg )
+
+
+
+ Bi-level optimizer for periodic boundary condition meshes (PbcMesh ).
+
+
+
+
+
+
+
+ PbcBilevelOptimizer ()
+
+ View Source
+
+
+
+
25 def __init__ ( self ) -> None :
+26 """Create a Bi-level optimizer for periodic boundary condition meshes with default parameters.
+27
+28 Does not specify the inner and outer loss yet.
+29 """
+30 super () . __init__ ()
+31 self . _inner_opt_func = self . _inner_opt
+32 self . _outer_opt_func = self . _outer_opt
+
+
+
+
Create a Bi-level optimizer for periodic boundary condition meshes with default parameters.
+
+
Does not specify the inner and outer loss yet.
+
+
+
+
+
+
+
+
+
def
+
compute_outer_loss ( self , mesh : Mesh , only_on_vertices : None | list [ int ] = None , only_on_edges : None | list [ int ] = None , only_on_faces : None | list [ int ] = None ) -> float :
+
+
View Source
+
+
+
+
44 def compute_outer_loss (
+45 self ,
+46 mesh : Mesh ,
+47 only_on_vertices : None | list [ int ] = None ,
+48 only_on_edges : None | list [ int ] = None ,
+49 only_on_faces : None | list [ int ] = None ,
+50 ) -> float :
+51 """Get the result of self.loss_function_outer called with the correct arguments."""
+52 selected_vertices , selected_edges , selected_faces = self . _selection_to_jax_arrays (
+53 only_on_vertices , only_on_edges , only_on_faces
+54 )
+55 if not isinstance ( mesh , PbcMesh ):
+56 msg = "The mesh given to a PbcBilevelOptimizer must be a PbcMesh."
+57 raise ValueError ( msg )
+58 elif self . loss_function_outer is None :
+59 msg = "The outer loss function was not defined."
+60 raise AttributeError ( msg )
+61 return float (
+62 self . loss_function_outer (
+63 mesh . vertices ,
+64 mesh . edges ,
+65 mesh . faces ,
+66 mesh . width ,
+67 mesh . height ,
+68 self . vertices_target ,
+69 self . edges_target ,
+70 self . faces_target ,
+71 selected_vertices ,
+72 selected_edges ,
+73 selected_faces ,
+74 self . image_target ,
+75 )
+76 )
+
+
+
+
Get the result of self.loss_function_outer called with the correct arguments.
+
+
+
+
+
+
+
+
+
def
+
compute_inner_loss ( self , mesh : Mesh , only_on_vertices : None | list [ int ] = None , only_on_edges : None | list [ int ] = None , only_on_faces : None | list [ int ] = None ) -> float :
+
+
View Source
+
+
+
+
78 def compute_inner_loss (
+ 79 self ,
+ 80 mesh : Mesh ,
+ 81 only_on_vertices : None | list [ int ] = None , # noqa: ARG002
+ 82 only_on_edges : None | list [ int ] = None , # noqa: ARG002
+ 83 only_on_faces : None | list [ int ] = None , # noqa: ARG002
+ 84 ) -> float :
+ 85 """Get the result of self.loss_function_inner called with the correct arguments."""
+ 86 if not isinstance ( mesh , PbcMesh ):
+ 87 msg = "The mesh given to a PbcBilevelOptimizer must be a PbcMesh."
+ 88 raise ValueError ( msg )
+ 89 elif self . loss_function_inner is None :
+ 90 msg = "The inner loss function was not defined."
+ 91 raise AttributeError ( msg )
+ 92 return float (
+ 93 self . loss_function_inner (
+ 94 mesh . vertices ,
+ 95 mesh . edges ,
+ 96 mesh . faces ,
+ 97 mesh . vertices_params ,
+ 98 mesh . edges_params ,
+ 99 mesh . faces_params ,
+100 )
+101 )
+
+
+
+
Get the result of self.loss_function_inner called with the correct arguments.
+
+
+
+
+
+
+
+
+
+ 23 class BoundedBilevelOptimizer ( _BilevelOptimizer ):
+ 24 """Bi-level optimizer for bounded meshes (`BoundedMesh`)."""
+ 25
+ 26 def __init__ ( self ) -> None :
+ 27 """Create a Bi-level optimizer for bounded meshes with default parameters.
+ 28
+ 29 Does not set the inner and outer loss functions yet.
+ 30 """
+ 31 super () . __init__ ()
+ 32 self . _inner_opt_func = self . _inner_opt
+ 33 self . _outer_opt_func = self . _outer_opt
+ 34 self . angles_target = jnp . array ([])
+ 35
+ 36 def _set_update_T1_func ( self , b : bool ) -> None : # noqa: N802
+ 37 """Set the _update_T1_func callable with respect to whether it is needed or not.
+ 38
+ 39 Must be implemented by child classes.
+ 40 """
+ 41 if b :
+ 42 self . _update_T1_func = update_T1_bounded
+ 43 else :
+ 44 self . _update_T1_func = do_not_update_T1_bounded
+ 45
+ 46 def compute_outer_loss (
+ 47 self ,
+ 48 mesh : Mesh ,
+ 49 only_on_vertices : None | list [ int ] = None ,
+ 50 only_on_edges : None | list [ int ] = None ,
+ 51 only_on_faces : None | list [ int ] = None ,
+ 52 ) -> float :
+ 53 """Get the result of self.loss_function_outer called with the correct arguments.
+ 54
+ 55 Must be implemented by child classes.
+ 56 """
+ 57 selected_vertices , selected_edges , selected_faces = self . _selection_to_jax_arrays (
+ 58 only_on_vertices , only_on_edges , only_on_faces
+ 59 )
+ 60 if not isinstance ( mesh , BoundedMesh ):
+ 61 msg = "The mesh given to a BoundedBilevelOptimizer must be a BoundedMesh."
+ 62 raise ValueError ( msg )
+ 63 elif self . loss_function_outer is None :
+ 64 msg = "The outer loss function was not defined."
+ 65 raise AttributeError ( msg )
+ 66 return float (
+ 67 self . loss_function_outer (
+ 68 mesh . vertices ,
+ 69 mesh . angles ,
+ 70 mesh . edges ,
+ 71 mesh . faces ,
+ 72 self . vertices_target ,
+ 73 self . angles_target ,
+ 74 self . edges_target ,
+ 75 self . faces_target ,
+ 76 selected_vertices ,
+ 77 selected_edges ,
+ 78 selected_faces ,
+ 79 self . image_target ,
+ 80 )
+ 81 )
+ 82
+ 83 def compute_inner_loss (
+ 84 self ,
+ 85 mesh : Mesh ,
+ 86 only_on_vertices : None | list [ int ] = None ,
+ 87 only_on_edges : None | list [ int ] = None ,
+ 88 only_on_faces : None | list [ int ] = None ,
+ 89 ) -> float :
+ 90 """Get the result of self.loss_function_inner called with the correct arguments.
+ 91
+ 92 Must be implemented by child classes.
+ 93 """
+ 94 selected_vertices , selected_edges , selected_faces = self . _selection_to_jax_arrays (
+ 95 only_on_vertices , only_on_edges , only_on_faces
+ 96 )
+ 97 if not isinstance ( mesh , BoundedMesh ):
+ 98 msg = "The mesh given to a BoundedBilevelOptimizer must be a BoundedMesh."
+ 99 raise ValueError ( msg )
+100 elif self . loss_function_inner is None :
+101 msg = "The inner loss function was not defined."
+102 raise AttributeError ( msg )
+103 return float (
+104 self . loss_function_inner (
+105 mesh . vertices ,
+106 mesh . angles ,
+107 mesh . edges ,
+108 mesh . faces ,
+109 selected_vertices ,
+110 selected_edges ,
+111 selected_faces ,
+112 mesh . vertices_params ,
+113 mesh . edges_params ,
+114 mesh . faces_params ,
+115 )
+116 )
+117
+118 def _inner_opt (
+119 self ,
+120 mesh : Mesh ,
+121 only_on_vertices : None | Array = None ,
+122 only_on_edges : None | Array = None ,
+123 only_on_faces : None | Array = None ,
+124 ) -> list :
+125 """Call the correct inner optimization function for a BoundedMesh."""
+126 if not isinstance ( mesh , BoundedMesh ):
+127 msg = "The mesh given to a BoundedBilevelOptimizer must be a BoundedMesh."
+128 raise ValueError ( msg )
+129 elif self . loss_function_inner is None :
+130 msg = "The inner loss function was not defined."
+131 raise AttributeError ( msg )
+132 elif self . _update_T1_func is None :
+133 msg = "The update T1 method was not set by a boolean."
+134 raise AttributeError ( msg )
+135 else :
+136 ( mesh . vertices , mesh . angles , mesh . edges , mesh . faces ), loss_history = inner_opt_bounded (
+137 vertTable = mesh . vertices ,
+138 angTable = mesh . angles ,
+139 heTable = mesh . edges ,
+140 faceTable = mesh . faces ,
+141 vert_params = mesh . vertices_params ,
+142 he_params = mesh . edges_params ,
+143 face_params = mesh . faces_params ,
+144 L_in = self . loss_function_inner ,
+145 solver = self . inner_solver ,
+146 min_dist_T1 = self . min_dist_T1 ,
+147 iterations_max = self . max_nb_iterations ,
+148 tolerance = self . tolerance ,
+149 patience = self . patience ,
+150 selected_verts = only_on_vertices ,
+151 selected_hes = only_on_edges ,
+152 selected_faces = only_on_faces ,
+153 update_T1_func = self . _update_T1_func ,
+154 )
+155 return list ( loss_history )
+156
+157 def _outer_opt (
+158 self ,
+159 mesh : Mesh ,
+160 selected_vertices : None | Array = None ,
+161 selected_edges : None | Array = None ,
+162 selected_faces : None | Array = None ,
+163 ) -> None :
+164 """Call the correct outer optimization function for a BoundedMesh."""
+165 if not isinstance ( mesh , BoundedMesh ):
+166 msg = "The mesh given to a BoundedBilevelOptimizer must be a BoundedMesh."
+167 raise ValueError ( msg )
+168 elif self . loss_function_inner is None :
+169 msg = "The inner loss function was not defined."
+170 raise AttributeError ( msg )
+171 elif self . loss_function_outer is None :
+172 msg = "The outer loss function was not defined."
+173 raise AttributeError ( msg )
+174 elif self . _update_T1_func is None :
+175 msg = "The update T1 method was not set by a boolean."
+176 raise AttributeError ( msg )
+177 else :
+178 match self . bilevel_optimization_method :
+179 case BilevelOptimizationMethod . AUTOMATIC_DIFFERENTIATION :
+180 mesh . vertices_params , mesh . edges_params , mesh . faces_params = outer_opt_bounded (
+181 mesh . vertices ,
+182 mesh . angles ,
+183 mesh . edges ,
+184 mesh . faces ,
+185 mesh . vertices_params ,
+186 mesh . edges_params ,
+187 mesh . faces_params ,
+188 self . vertices_target ,
+189 self . angles_target ,
+190 self . edges_target ,
+191 self . faces_target ,
+192 self . loss_function_inner ,
+193 self . loss_function_outer ,
+194 self . inner_solver ,
+195 self . outer_solver ,
+196 self . min_dist_T1 ,
+197 self . max_nb_iterations ,
+198 self . tolerance ,
+199 self . patience ,
+200 selected_vertices ,
+201 selected_edges ,
+202 selected_faces ,
+203 self . image_target ,
+204 self . _update_T1_func ,
+205 )
+206
+207 case BilevelOptimizationMethod . EQUILIBRIUM_PROPAGATION :
+208 mesh . vertices_params , mesh . edges_params , mesh . faces_params = outer_eq_prop_bounded (
+209 mesh . vertices ,
+210 mesh . angles ,
+211 mesh . edges ,
+212 mesh . faces ,
+213 mesh . vertices_params ,
+214 mesh . edges_params ,
+215 mesh . faces_params ,
+216 self . vertices_target ,
+217 self . angles_target ,
+218 self . edges_target ,
+219 self . faces_target ,
+220 self . loss_function_inner ,
+221 self . loss_function_outer ,
+222 self . inner_solver ,
+223 self . outer_solver ,
+224 self . min_dist_T1 ,
+225 self . max_nb_iterations ,
+226 self . tolerance ,
+227 self . patience ,
+228 selected_vertices ,
+229 selected_edges ,
+230 selected_faces ,
+231 self . image_target ,
+232 self . beta ,
+233 self . _update_T1_func ,
+234 )
+235
+236 case BilevelOptimizationMethod . IMPLICIT_DIFFERENTIATION :
+237 mesh . vertices_params , mesh . edges_params , mesh . faces_params = outer_implicit_bounded (
+238 mesh . vertices ,
+239 mesh . angles ,
+240 mesh . edges ,
+241 mesh . faces ,
+242 mesh . vertices_params ,
+243 mesh . edges_params ,
+244 mesh . faces_params ,
+245 self . vertices_target ,
+246 self . angles_target ,
+247 self . edges_target ,
+248 self . faces_target ,
+249 self . loss_function_inner ,
+250 self . loss_function_outer ,
+251 self . inner_solver ,
+252 self . outer_solver ,
+253 self . min_dist_T1 ,
+254 self . max_nb_iterations ,
+255 self . tolerance ,
+256 self . patience ,
+257 selected_vertices ,
+258 selected_edges ,
+259 selected_faces ,
+260 self . image_target ,
+261 self . _update_T1_func ,
+262 )
+263 case BilevelOptimizationMethod . ADJOINT_STATE :
+264 mesh . vertices_params , mesh . edges_params , mesh . faces_params = outer_adjoint_state_bounded (
+265 mesh . vertices ,
+266 mesh . angles ,
+267 mesh . edges ,
+268 mesh . faces ,
+269 mesh . vertices_params ,
+270 mesh . edges_params ,
+271 mesh . faces_params ,
+272 self . vertices_target ,
+273 self . angles_target ,
+274 self . edges_target ,
+275 self . faces_target ,
+276 self . loss_function_inner ,
+277 self . loss_function_outer ,
+278 self . inner_solver ,
+279 self . outer_solver ,
+280 self . min_dist_T1 ,
+281 self . max_nb_iterations ,
+282 self . tolerance ,
+283 self . patience ,
+284 selected_vertices ,
+285 selected_edges ,
+286 selected_faces ,
+287 self . image_target ,
+288 self . _update_T1_func ,
+289 )
+290 case _ :
+291 msg = f "Method not recognized. Must be a BilevelOptimizationMethod. \
+292 Got { self . bilevel_optimization_method } ."
+293 raise AttributeError ( msg )
+
+
+
+
+
+
+
+
+
+
+ BoundedBilevelOptimizer ()
+
+ View Source
+
+
+
+
26 def __init__ ( self ) -> None :
+27 """Create a Bi-level optimizer for bounded meshes with default parameters.
+28
+29 Does not set the inner and outer loss functions yet.
+30 """
+31 super () . __init__ ()
+32 self . _inner_opt_func = self . _inner_opt
+33 self . _outer_opt_func = self . _outer_opt
+34 self . angles_target = jnp . array ([])
+
+
+
+
Create a Bi-level optimizer for bounded meshes with default parameters.
+
+
Does not set the inner and outer loss functions yet.
+
+
+
+
+
+
+ angles_target
+
+
+
+
+
+
+
+
+
+
+
+
+
def
+
compute_outer_loss ( self , mesh : Mesh , only_on_vertices : None | list [ int ] = None , only_on_edges : None | list [ int ] = None , only_on_faces : None | list [ int ] = None ) -> float :
+
+
View Source
+
+
+
+
46 def compute_outer_loss (
+47 self ,
+48 mesh : Mesh ,
+49 only_on_vertices : None | list [ int ] = None ,
+50 only_on_edges : None | list [ int ] = None ,
+51 only_on_faces : None | list [ int ] = None ,
+52 ) -> float :
+53 """Get the result of self.loss_function_outer called with the correct arguments.
+54
+55 Must be implemented by child classes.
+56 """
+57 selected_vertices , selected_edges , selected_faces = self . _selection_to_jax_arrays (
+58 only_on_vertices , only_on_edges , only_on_faces
+59 )
+60 if not isinstance ( mesh , BoundedMesh ):
+61 msg = "The mesh given to a BoundedBilevelOptimizer must be a BoundedMesh."
+62 raise ValueError ( msg )
+63 elif self . loss_function_outer is None :
+64 msg = "The outer loss function was not defined."
+65 raise AttributeError ( msg )
+66 return float (
+67 self . loss_function_outer (
+68 mesh . vertices ,
+69 mesh . angles ,
+70 mesh . edges ,
+71 mesh . faces ,
+72 self . vertices_target ,
+73 self . angles_target ,
+74 self . edges_target ,
+75 self . faces_target ,
+76 selected_vertices ,
+77 selected_edges ,
+78 selected_faces ,
+79 self . image_target ,
+80 )
+81 )
+
+
+
+
Get the result of self.loss_function_outer called with the correct arguments.
+
+
Must be implemented by child classes.
+
+
+
+
+
+
+
+
+
def
+
compute_inner_loss ( self , mesh : Mesh , only_on_vertices : None | list [ int ] = None , only_on_edges : None | list [ int ] = None , only_on_faces : None | list [ int ] = None ) -> float :
+
+
View Source
+
+
+
+
83 def compute_inner_loss (
+ 84 self ,
+ 85 mesh : Mesh ,
+ 86 only_on_vertices : None | list [ int ] = None ,
+ 87 only_on_edges : None | list [ int ] = None ,
+ 88 only_on_faces : None | list [ int ] = None ,
+ 89 ) -> float :
+ 90 """Get the result of self.loss_function_inner called with the correct arguments.
+ 91
+ 92 Must be implemented by child classes.
+ 93 """
+ 94 selected_vertices , selected_edges , selected_faces = self . _selection_to_jax_arrays (
+ 95 only_on_vertices , only_on_edges , only_on_faces
+ 96 )
+ 97 if not isinstance ( mesh , BoundedMesh ):
+ 98 msg = "The mesh given to a BoundedBilevelOptimizer must be a BoundedMesh."
+ 99 raise ValueError ( msg )
+100 elif self . loss_function_inner is None :
+101 msg = "The inner loss function was not defined."
+102 raise AttributeError ( msg )
+103 return float (
+104 self . loss_function_inner (
+105 mesh . vertices ,
+106 mesh . angles ,
+107 mesh . edges ,
+108 mesh . faces ,
+109 selected_vertices ,
+110 selected_edges ,
+111 selected_faces ,
+112 mesh . vertices_params ,
+113 mesh . edges_params ,
+114 mesh . faces_params ,
+115 )
+116 )
+
+
+
+
Get the result of self.loss_function_inner called with the correct arguments.
+
+
Must be implemented by child classes.
+
+
+
+
+
+
+
+
+
+
def
+
plot_mesh ( mesh : Mesh , vertex_plot : VertexPlot = < VertexPlot.INVISIBLE : 3 > , edge_plot : EdgePlot = < EdgePlot.BLACK : 1 > , face_plot : FacePlot = < FacePlot.MULTICOLOR : 1 > , vertex_parameters_name : str = '' , edge_parameters_name : str = '' , face_parameters_name : str = '' , show : bool = True , save : bool = False , save_path : str = 'pbc_mesh.png' , faces_cmap_name : str = 'cividis' , edges_cmap_name : str = 'coolwarm' , edges_width : float = 2 , vertices_cmap_name : str = 'spring' , vertices_size : float = 20 , title : str = '' , forced_vertex_scale : tuple [ float , float ] | None = None , forced_edge_scale : tuple [ float , float ] | None = None , forced_face_scale : tuple [ float , float ] | None = None ) -> None :
+
+
View Source
+
+
+
+ 108 def plot_mesh (
+109 mesh : Mesh ,
+110 vertex_plot : VertexPlot = VertexPlot . INVISIBLE ,
+111 edge_plot : EdgePlot = EdgePlot . BLACK ,
+112 face_plot : FacePlot = FacePlot . MULTICOLOR ,
+113 vertex_parameters_name : str = "" ,
+114 edge_parameters_name : str = "" ,
+115 face_parameters_name : str = "" ,
+116 show : bool = True ,
+117 save : bool = False ,
+118 save_path : str = "pbc_mesh.png" ,
+119 faces_cmap_name : str = "cividis" ,
+120 edges_cmap_name : str = "coolwarm" ,
+121 edges_width : float = 2 ,
+122 vertices_cmap_name : str = "spring" ,
+123 vertices_size : float = 20 ,
+124 title : str = "" ,
+125 forced_vertex_scale : tuple [ float , float ] | None = None ,
+126 forced_edge_scale : tuple [ float , float ] | None = None ,
+127 forced_face_scale : tuple [ float , float ] | None = None ,
+128 ) -> None :
+129 """Plot the mesh and decide to save and/or show the mesh or not."""
+130 if isinstance ( mesh , PbcMesh ):
+131 _plot_pbc_mesh (
+132 mesh ,
+133 vertex_plot ,
+134 edge_plot ,
+135 face_plot ,
+136 vertex_parameters_name ,
+137 edge_parameters_name ,
+138 face_parameters_name ,
+139 show ,
+140 save ,
+141 save_path ,
+142 faces_cmap_name ,
+143 edges_cmap_name ,
+144 edges_width ,
+145 vertices_cmap_name ,
+146 vertices_size ,
+147 title ,
+148 forced_vertex_scale ,
+149 forced_edge_scale ,
+150 forced_face_scale ,
+151 )
+152 elif isinstance ( mesh , BoundedMesh ):
+153 _plot_bounded_mesh (
+154 mesh ,
+155 vertex_plot ,
+156 edge_plot ,
+157 face_plot ,
+158 vertex_parameters_name ,
+159 edge_parameters_name ,
+160 face_parameters_name ,
+161 show ,
+162 save ,
+163 save_path ,
+164 faces_cmap_name ,
+165 edges_cmap_name ,
+166 edges_width ,
+167 vertices_cmap_name ,
+168 vertices_size ,
+169 title ,
+170 forced_vertex_scale ,
+171 forced_edge_scale ,
+172 forced_face_scale ,
+173 )
+
+
+
+ Plot the mesh and decide to save and/or show the mesh or not.
+
+
+
+
+
+
+
+
+
def
+
get_plot_mesh ( mesh : Mesh , vertex_plot : VertexPlot = < VertexPlot.INVISIBLE : 3 > , edge_plot : EdgePlot = < EdgePlot.BLACK : 1 > , face_plot : FacePlot = < FacePlot.MULTICOLOR : 1 > , vertex_parameters_name : str = '' , edge_parameters_name : str = '' , face_parameters_name : str = '' , faces_cmap_name : str = 'cividis' , edges_cmap_name : str = 'coolwarm' , edges_width : float = 2 , vertices_cmap_name : str = 'spring' , vertices_size : float = 20 , title : str = '' , forced_vertex_scale : tuple [ float , float ] | None = None , forced_edge_scale : tuple [ float , float ] | None = None , forced_face_scale : tuple [ float , float ] | None = None ) -> tuple [ matplotlib . figure . Figure , matplotlib . axes . _axes . Axes ] :
+
+
View Source
+
+
+
+ 176 def get_plot_mesh (
+177 mesh : Mesh ,
+178 vertex_plot : VertexPlot = VertexPlot . INVISIBLE ,
+179 edge_plot : EdgePlot = EdgePlot . BLACK ,
+180 face_plot : FacePlot = FacePlot . MULTICOLOR ,
+181 vertex_parameters_name : str = "" ,
+182 edge_parameters_name : str = "" ,
+183 face_parameters_name : str = "" ,
+184 faces_cmap_name : str = "cividis" ,
+185 edges_cmap_name : str = "coolwarm" ,
+186 edges_width : float = 2 ,
+187 vertices_cmap_name : str = "spring" ,
+188 vertices_size : float = 20 ,
+189 title : str = "" ,
+190 forced_vertex_scale : tuple [ float , float ] | None = None ,
+191 forced_edge_scale : tuple [ float , float ] | None = None ,
+192 forced_face_scale : tuple [ float , float ] | None = None ,
+193 ) -> tuple [ Figure , Axes ]:
+194 """Get the matplotlib figure and and ax for one plot."""
+195 if isinstance ( mesh , PbcMesh ):
+196 return _get_plot_pbc_mesh (
+197 mesh ,
+198 vertex_plot ,
+199 edge_plot ,
+200 face_plot ,
+201 vertex_parameters_name ,
+202 edge_parameters_name ,
+203 face_parameters_name ,
+204 faces_cmap_name ,
+205 edges_cmap_name ,
+206 edges_width ,
+207 vertices_cmap_name ,
+208 vertices_size ,
+209 title ,
+210 forced_vertex_scale ,
+211 forced_edge_scale ,
+212 forced_face_scale ,
+213 )
+214 elif isinstance ( mesh , BoundedMesh ):
+215 return _get_plot_bounded_mesh (
+216 mesh ,
+217 vertex_plot ,
+218 edge_plot ,
+219 face_plot ,
+220 vertex_parameters_name ,
+221 edge_parameters_name ,
+222 face_parameters_name ,
+223 faces_cmap_name ,
+224 edges_cmap_name ,
+225 edges_width ,
+226 vertices_cmap_name ,
+227 vertices_size ,
+228 title ,
+229 forced_vertex_scale ,
+230 forced_edge_scale ,
+231 forced_face_scale ,
+232 )
+233 else :
+234 msg = f "Expected either a PbcMesh or a BoundedMesh. Got { mesh } instead."
+235 raise ValueError ( msg )
+
+
+
+ Get the matplotlib figure and and ax for one plot.
+
+
+
+
+
+
+
+
+ class
+ FacePlot (enum.Enum ):
+
+ View Source
+
+
+
+ 25 class FacePlot ( Enum ):
+26 """What it is possible to show on a face."""
+27
+28 MULTICOLOR = 1
+29 """Each face get a random color."""
+30 FACE_PARAMETER = 2
+31 """The face color depends on its parameter."""
+32 AREA = 3
+33 """The face color depends on its area."""
+34 PERIMETER = 4
+35 """The face color depends on its perimeter."""
+36 WHITE = 5
+37 """All faces are just white."""
+38 FATES = 6
+39 """Faces are colored depending on their fate marker (if any)."""
+
+
+
+ What it is possible to show on a face.
+
+
+
+
+
+
+
+
Each face get a random color.
+
+
+
+
+
+
+
+
+
The face color depends on its parameter.
+
+
+
+
+
+
+
+
+
The face color depends on its area.
+
+
+
+
+
+
+
+
+
The face color depends on its perimeter.
+
+
+
+
+
+
+
+
+
All faces are just white.
+
+
+
+
+
+
+
+
+
Faces are colored depending on their fate marker (if any).
+
+
+
+
+
+
+
+
+
+ class
+ EdgePlot (enum.Enum ):
+
+ View Source
+
+
+
+ 42 class EdgePlot ( Enum ):
+43 """What it is possible to show on an edge."""
+44
+45 BLACK = 1
+46 """All edges are black."""
+47 EDGE_PARAMETER = 2
+48 """The edge color depends on its parameter."""
+49 LENGTH = 3
+50 """The edge color depends on its length."""
+51 INVISIBLE = 4
+52 """Do not show edges."""
+
+
+
+ What it is possible to show on an edge.
+
+
+
+
+
+
+
+
+
The edge color depends on its parameter.
+
+
+
+
+
+
+
+
+
The edge color depends on its length.
+
+
+
+
+
+
+
+
+
+
+ class
+ VertexPlot (enum.Enum ):
+
+ View Source
+
+
+
+ 55 class VertexPlot ( Enum ):
+56 """What it is possible to show on a vertex."""
+57
+58 BLACK = 1
+59 """Vertices are black."""
+60 VERTEX_PARAMETER = 2
+61 """The vertex color depends on its parameter."""
+62 INVISIBLE = 3
+63 """Do not show vertices."""
+
+
+
+ What it is possible to show on a vertex.
+
+
+
+
+
+
+
+
+
The vertex color depends on its parameter.
+
+
+
+
+
+
+
+
+
+
@partial(jit, static_argnums=(3, 4))
+
+
def
+
cost_v2v ( vertTable : jax . Array , heTable : jax . Array , faceTable : jax . Array , width : float , height : float , vertTable_target : jax . Array , _heTable_target : jax . Array , _faceTable_target : jax . Array , selected_verts : jax . Array | None = None , selected_hes : jax . Array | None = None , selected_faces : jax . Array | None = None , _image_target : jax . Array | None = None ) -> jax . Array :
+
+
View Source
+
+
+
+ 217 @partial ( jit , static_argnums = ( 3 , 4 ))
+218 def cost_v2v (
+219 vertTable : Array ,
+220 heTable : Array ,
+221 faceTable : Array ,
+222 width : float ,
+223 height : float ,
+224 vertTable_target : Array ,
+225 _heTable_target : Array ,
+226 _faceTable_target : Array ,
+227 selected_verts : Array | None = None ,
+228 selected_hes : Array | None = None ,
+229 selected_faces : Array | None = None ,
+230 _image_target : Array | None = None ,
+231 ) -> Array :
+232 """Cost vertex to vertex. Compare the positions of given vertices to target vertices (`PbcMesh`)."""
+233 if selected_verts is None :
+234 selected_verts = jnp . arange ( vertTable . shape [ 0 ])
+235 if selected_hes is None :
+236 selected_hes = jnp . arange ( heTable . shape [ 0 ])
+237 if selected_faces is None :
+238 selected_faces = jnp . arange ( faceTable . shape [ 0 ])
+239
+240 def squared_distance ( v : Array , vertTable : Array , vertTable_target : Array , width : float , height : float ) -> Array :
+241 return (
+242 jnp . min (
+243 jnp . array (
+244 [
+245 (
+246 ( vertTable [ v ][ 0 ] - vertTable_target [ v ][ 0 ]) ** 2
+247 + ( vertTable [ v ][ 1 ] - vertTable_target [ v ][ 1 ]) ** 2
+248 )
+249 ** 0.5 ,
+250 (
+251 ( vertTable [ v ][ 0 ] - ( vertTable_target [ v ][ 0 ] + width )) ** 2
+252 + ( vertTable [ v ][ 1 ] - vertTable_target [ v ][ 1 ]) ** 2
+253 )
+254 ** 0.5 ,
+255 (
+256 ( vertTable [ v ][ 0 ] - ( vertTable_target [ v ][ 0 ] - width )) ** 2
+257 + ( vertTable [ v ][ 1 ] - vertTable_target [ v ][ 1 ]) ** 2
+258 )
+259 ** 0.5 ,
+260 (
+261 ( vertTable [ v ][ 0 ] - vertTable_target [ v ][ 0 ]) ** 2
+262 + ( vertTable [ v ][ 1 ] - ( vertTable_target [ v ][ 1 ] + height )) ** 2
+263 )
+264 ** 0.5 ,
+265 (
+266 ( vertTable [ v ][ 0 ] - vertTable_target [ v ][ 0 ]) ** 2
+267 + ( vertTable [ v ][ 1 ] - ( vertTable_target [ v ][ 1 ] - height )) ** 2
+268 )
+269 ** 0.5 ,
+270 (
+271 ( vertTable [ v ][ 0 ] - ( vertTable_target [ v ][ 0 ] + width )) ** 2
+272 + ( vertTable [ v ][ 1 ] - ( vertTable_target [ v ][ 1 ] + height )) ** 2
+273 )
+274 ** 0.5 ,
+275 (
+276 ( vertTable [ v ][ 0 ] - ( vertTable_target [ v ][ 0 ] + width )) ** 2
+277 + ( vertTable [ v ][ 1 ] - ( vertTable_target [ v ][ 1 ] - height )) ** 2
+278 )
+279 ** 0.5 ,
+280 (
+281 ( vertTable [ v ][ 0 ] - ( vertTable_target [ v ][ 0 ] - width )) ** 2
+282 + ( vertTable [ v ][ 1 ] - ( vertTable_target [ v ][ 1 ] + height )) ** 2
+283 )
+284 ** 0.5 ,
+285 (
+286 ( vertTable [ v ][ 0 ] - ( vertTable_target [ v ][ 0 ] - width )) ** 2
+287 + ( vertTable [ v ][ 1 ] - ( vertTable_target [ v ][ 1 ] - height )) ** 2
+288 )
+289 ** 0.5 ,
+290 ]
+291 )
+292 )
+293 ) ** 2
+294
+295 def mapped_fn ( v : Array ) -> Array :
+296 return squared_distance ( v , vertTable , vertTable_target , width , height )
+297
+298 distances = vmap ( mapped_fn )( selected_verts )
+299
+300 return ( 1.0 / ( 2 * len ( distances ))) * jnp . sum ( distances )
+
+
+
+ Cost vertex to vertex. Compare the positions of given vertices to target vertices (PbcMesh ).
+
+
+
+
+
+
+
+
@jit
+
+
def
+
cost_mesh2image ( vertTable : jax . Array , heTable : jax . Array , _faceTable : jax . Array , width : float , height : float , _vertTable_target : jax . Array , _heTable_target : jax . Array , _faceTable_target : jax . Array , _selected_verts : jax . Array , selected_hes : jax . Array , _selected_faces : jax . Array , image_target : jax . Array ) -> jax . Array :
+
+
View Source
+
+
+
+ 303 @jit
+304 def cost_mesh2image (
+305 vertTable : Array ,
+306 heTable : Array ,
+307 _faceTable : Array ,
+308 width : float ,
+309 height : float ,
+310 _vertTable_target : Array ,
+311 _heTable_target : Array ,
+312 _faceTable_target : Array ,
+313 _selected_verts : Array ,
+314 selected_hes : Array ,
+315 _selected_faces : Array ,
+316 image_target : Array ,
+317 ) -> Array :
+318 """Cost mesh to image. Compare the given vertices positions to a target image (`PbcMesh`)."""
+319 wh = jnp . asarray ([ width , height ])
+320 starting = ( vertTable [ heTable [ selected_hes , 3 ], : 2 ]) * 2 / wh # (M, 2)
+321 # ending = (vertTable[heTable[selected_hes, 4], :2]) * 2 / L_box # (M, 2)
+322 ending = (
+323 (
+324 vertTable [ heTable [ selected_hes , 4 ], : 2 ]
+325 + jnp . stack (
+326 [
+327 heTable [ selected_hes , 6 ],
+328 heTable [ selected_hes , 7 ],
+329 ],
+330 axis =- 1 ,
+331 )
+332 * wh
+333 )
+334 * 2
+335 / wh
+336 )
+337
+338 he_edges = stack (( starting , ending ), axis = 1 ) # (N, 2, 2)
+339 x = he_edges . transpose ( 1 , 2 , 0 ) - 1 # (2, 2, N)
+340
+341 # Blur
+342 image = _gaussian_blur_line_segments ( x ) . real
+343
+344 # Normalization
+345 image = image / image . sum ()
+346 image_target = image_target / image_target . sum ()
+347
+348 l2_norm = jnp . linalg . norm ( jnp . sqrt ( jnp . sum ((( image - image_target ) ** 2 ) * ( 1 - image_target ), axis =- 1 )) . flatten ())
+349
+350 return l2_norm
+
+
+
+ Cost mesh to image. Compare the given vertices positions to a target image (PbcMesh ).
+
+
+
+
+
+
+
+
@jit
+
+
def
+
cost_areas ( vertTable : jax . Array , heTable : jax . Array , faceTable : jax . Array , width : float , height : float , _selected_verts : jax . Array , _selected_hes : jax . Array , selected_faces : jax . Array , vertTable_target : jax . Array , heTable_target : jax . Array , faceTable_target : jax . Array , _image_target : jax . Array ) -> jax . Array :
+
+
View Source
+
+
+
+ 353 @jit
+354 def cost_areas (
+355 vertTable : Array ,
+356 heTable : Array ,
+357 faceTable : Array ,
+358 width : float ,
+359 height : float ,
+360 _selected_verts : Array ,
+361 _selected_hes : Array ,
+362 selected_faces : Array ,
+363 vertTable_target : Array ,
+364 heTable_target : Array ,
+365 faceTable_target : Array ,
+366 _image_target : Array ,
+367 ) -> Array :
+368 """Cost areas : compare the areas of the given mesh versus the areas of the target mesh (`PbcMesh`)."""
+369
+370 def mapped_fn ( f : Array ) -> Array :
+371 return (
+372 get_area ( f , vertTable , heTable , faceTable , width , height )
+373 - get_area ( f , vertTable_target , heTable_target , faceTable_target , width , height )
+374 ) ** 2 # + \
+375
+376 difference = vmap ( mapped_fn )( selected_faces )
+377 return ( 1.0 / len ( difference )) * jnp . sum ( difference )
+
+
+
+ Cost areas : compare the areas of the given mesh versus the areas of the target mesh (PbcMesh ).
+
+
+
+
+
+
+
+
@partial(jit, static_argnums=(3, 4))
+
+
def
+
cost_IAS ( vertTable : jax . Array , heTable : jax . Array , faceTable : jax . Array , _width : float , _height : float , vertTable_target : jax . Array , heTable_target : jax . Array , faceTable_target : jax . Array , _selected_verts : jax . Array | None = None , _selected_hes : jax . Array | None = None , _selected_faces : jax . Array | None = None , _image_target : jax . Array | None = None ) -> jax . Array :
+
+
View Source
+
+
+
+ 466 @partial ( jit , static_argnums = ( 3 , 4 ))
+467 def cost_IAS ( # noqa: C901, N802
+468 vertTable : Array ,
+469 heTable : Array ,
+470 faceTable : Array ,
+471 _width : float ,
+472 _height : float ,
+473 vertTable_target : Array ,
+474 heTable_target : Array ,
+475 faceTable_target : Array ,
+476 _selected_verts : Array | None = None ,
+477 _selected_hes : Array | None = None ,
+478 _selected_faces : Array | None = None ,
+479 _image_target : Array | None = None ,
+480 ) -> Array :
+481 r """Differentiable Index Aware Structural Loss. Force to respect the topology.
+482
+483 C_{IAS}(i,j) = \sqrt{\sum_{k=1}^{N} (S_1(i,k) - S_2(j,k))^2}
+484 """
+485 L_box = jnp . sqrt ( len ( faceTable ))
+486
+487 def l2 ( x : Array , y : Array ) -> Array :
+488 diff = x [:, None , :] - y [ None , :, :]
+489 return jnp . sqrt ( jnp . sum ( diff ** 2 , axis =- 1 ) + 1e-12 )
+490
+491 def mse ( x : Array , y : Array ) -> Array :
+492 diff = x [:, None , :] - y [ None , :, :]
+493 return jnp . sum ( diff ** 2 , axis =- 1 ) + 1e-12
+494
+495 def sinkhorn ( a : Array , b : Array , C : Array , eps : float = 5e-2 , n_iters : int = 50 ) -> Array :
+496 K = jnp . exp ( - C / eps )
+497 u = jnp . ones_like ( a )
+498 v = jnp . ones_like ( b )
+499
+500 def body ( _ : int , state : tuple [ Array , Array ]) -> tuple [ Array , Array ]:
+501 u , v = state
+502 u = a / ( K @ v + 1e-12 )
+503 v = b / ( K . T @ u + 1e-12 )
+504 return ( u , v )
+505
+506 u , v = fori_loop ( 0 , n_iters , body , ( u , v ))
+507 return jnp . outer ( u , v ) * K
+508
+509 # dual graph from half-edges
+510 def build_dual_adj ( heTable : Array , n_faces : int ) -> Array :
+511 face = heTable [:, 5 ]
+512 twin = heTable [:, 2 ]
+513
+514 face_twin = face [ twin ]
+515
+516 # mask: 1.0 for valid edges, 0.0 for boundary/self
+517 mask = ( face != face_twin ) . astype ( jnp . float32 )
+518
+519 A = jnp . zeros (( n_faces , n_faces ))
+520
+521 # scatter with mask weighting (no boolean indexing)
+522 A = A . at [ face , face_twin ] . add ( mask )
+523 A = A . at [ face_twin , face ] . add ( mask )
+524
+525 # binarize (avoid double counts)
+526 A = jnp . clip ( A , 0.0 , 1.0 )
+527
+528 return A
+529
+530 # structure metric (1-hop + 2-hop)
+531 def structure_matrix ( A : Array ) -> Array :
+532 # A2 = A @ A
+533 # normalize to avoid scaling issues
+534 # return 2.0 - A - 0.5 * A2
+535 return 2.0 - A
+536
+537 def get_face_vertices (
+538 he_start : Array , heTable : Array , vertTable : Array , max_edges : int = 20
+539 ) -> tuple [ Array , Array ]:
+540 """Collect vertices of one face using half-edge traversal.
+541
+542 Returns fixed-size array (max_edges, 2) + mask
+543 """
+544 verts = jnp . zeros (( max_edges , 2 ))
+545 mask = jnp . zeros (( max_edges ,))
+546 offset = jnp . array ([ 0 , 0 ])
+547
+548 def body_fun ( i : int , state : tuple [ Array , Array , Array , Array ]) -> tuple [ Array , Array , Array , Array ]:
+549 he , verts , mask , offset = state
+550
+551 source = heTable [ he , 3 ] . astype ( jnp . int32 )
+552 off = heTable [ he , 6 : 8 ]
+553
+554 pos = vertTable [ source ] + offset * L_box # jnp.array([width, height])
+555
+556 verts = verts . at [ i ] . set ( pos )
+557 mask = mask . at [ i ] . set ( 1.0 )
+558
+559 offset += off
+560 he_next = heTable [ he , 1 ] . astype ( jnp . int32 )
+561
+562 return ( he_next , verts , mask , offset )
+563
+564 _he_final , verts , mask , offset = fori_loop ( 0 , max_edges , body_fun , ( he_start , verts , mask , offset ))
+565
+566 return verts , mask
+567
+568 def polygon_centroid ( verts : Array , mask : Array ) -> Array :
+569 """Compute centroid from masked polygon vertices."""
+570 # shift for edges
+571 v = verts
+572 v_next = jnp . roll ( v , - 1 , axis = 0 )
+573
+574 cross = v [:, 0 ] * v_next [:, 1 ] - v_next [:, 0 ] * v [:, 1 ]
+575 cross = cross * mask
+576
+577 area = jnp . sum ( cross ) / 2.0 + 1e-12
+578
+579 cx = jnp . sum (( v [:, 0 ] + v_next [:, 0 ]) * cross ) / ( 6 * area )
+580 cy = jnp . sum (( v [:, 1 ] + v_next [:, 1 ]) * cross ) / ( 6 * area )
+581
+582 return jnp . array ([ cx , cy ])
+583
+584 def compute_face_centroids ( faceTable : Array , heTable : Array , vertTable : Array ) -> Array :
+585 """Compute centroids for all faces."""
+586 he_start = faceTable [:] . astype ( jnp . int32 )
+587
+588 def single_face ( he : Array ) -> Array :
+589 verts , mask = get_face_vertices ( he , heTable , vertTable )
+590 return polygon_centroid ( verts , mask )
+591
+592 return vmap ( single_face )( he_start )
+593
+594 alpha = 0.0
+595 """ATTENTION !!!!!!!!!!!!"""
+596
+597 # face centroids
+598 X = compute_face_centroids ( faceTable , heTable , vertTable )
+599 Y = compute_face_centroids ( faceTable_target , heTable_target , vertTable_target )
+600
+601 N = X . shape [ 0 ]
+602 M = Y . shape [ 0 ]
+603
+604 # uniform weights
+605 a = jnp . ones ( N ) / N
+606 b = jnp . ones ( M ) / M
+607
+608 # geometry cost
+609 C_geom = l2 ( X , Y )
+610
+611 # structure cost
+612 A1 = build_dual_adj ( heTable , N )
+613 A2 = build_dual_adj ( heTable_target , M )
+614
+615 S1 = structure_matrix ( A1 )
+616 S2 = structure_matrix ( A2 )
+617
+618 # project structure into pairwise node cost
+619 # (cheap approximation of tem)
+620 # C_l2 = l2(S1, S2)
+621 C_mse = mse ( S1 , S2 )
+622
+623 # combined cost
+624 C_total = alpha * C_geom + ( 1.0 - alpha ) * C_mse
+625
+626 # transport
+627 gamma = sinkhorn ( a , b , C_total )
+628
+629 # final loss
+630 loss = jnp . sum ( gamma * C_total )
+631
+632 return loss
+
+
+
+ Differentiable Index Aware Structural Loss. Force to respect the topology.
+
+
C_{IAS}(i,j) = \sqrt{\sum_{k=1}^{N} (S_1(i,k) - S_2(j,k))^2}
+
+
+
+
+
+
+
+
+ def
+ cost_d_IAS ( _vertTable : jax . Array , heTable : jax . Array , faceTable : jax . Array , _width : float , _height : float , _vertTable_target : jax . Array , heTable_target : jax . Array , faceTable_target : jax . Array , _selected_verts : jax . Array | None = None , _selected_hes : jax . Array | None = None , _selected_faces : jax . Array | None = None , _image_target : jax . Array | None = None ) -> int :
+
+ View Source
+
+
+
+ 635 def cost_d_IAS ( # noqa: N802
+636 _vertTable : Array ,
+637 heTable : Array ,
+638 faceTable : Array ,
+639 _width : float ,
+640 _height : float ,
+641 _vertTable_target : Array ,
+642 heTable_target : Array ,
+643 faceTable_target : Array ,
+644 _selected_verts : Array | None = None ,
+645 _selected_hes : Array | None = None ,
+646 _selected_faces : Array | None = None ,
+647 _image_target : Array | None = None ,
+648 ) -> int :
+649 r """Discrete Index Aware Structural loss. Counts mismatched edges.
+650
+651 C_{d-IAS}(i,j) = \sum_{k=1}^{N} |A_1(i,k) - A_2(j,k)|
+652 """
+653
+654 def build_dual_adj_np ( heTable : Array , n_faces : int ) -> NDArray :
+655 face = heTable [:, 5 ]
+656 twin = heTable [:, 2 ]
+657
+658 face_twin = face [ twin ]
+659 mask = ( face != face_twin ) . astype ( float )
+660
+661 A = np . zeros (( n_faces , n_faces ))
+662
+663 A [ face , face_twin ] += mask
+664 A [ face_twin , face ] += mask
+665
+666 A = np . clip ( A , 0.0 , 1.0 )
+667 return A
+668
+669 n1 = len ( faceTable )
+670 n2 = len ( faceTable_target )
+671
+672 if n1 != n2 :
+673 msg = "This version requires same number of faces"
+674 raise ValueError ( msg )
+675
+676 n = n1
+677
+678 A1 = build_dual_adj_np ( heTable , n )
+679 A2 = build_dual_adj_np ( heTable_target , n )
+680
+681 # --- Step 1: node matching via Hungarian ---
+682 # cost between nodes = L1 difference of adjacency rows
+683 C = np . sum ( np . abs ( A1 [:, None , :] - A2 [ None , :, :]), axis =- 1 )
+684
+685 row_ind , col_ind = linear_sum_assignment ( C )
+686
+687 # permutation: face i in A1 ↔ face col_ind[i] in A2
+688 P = np . zeros (( n , n ))
+689 P [ row_ind , col_ind ] = 1.0
+690
+691 # --- Step 2: permute A2 ---
+692 A2_perm = P @ A2 @ P . T
+693
+694 # --- Step 3: count edge mismatches ---
+695 diff = np . abs ( A1 - A2_perm )
+696
+697 # count each edge once
+698 diff_upper = np . triu ( diff , k = 1 )
+699
+700 loss = int ( np . sum ( diff_upper ))
+701
+702 return loss
+
+
+
+ Discrete Index Aware Structural loss. Counts mismatched edges.
+
+
C_{d-IAS}(i,j) = \sum_{k=1}^{N} |A_1(i,k) - A_2(j,k)|
+
+
+
+
+
+
+
+
@partial(jit, static_argnums=(3, 4))
+
+
def
+
cost_tem_halfedge ( vertTable : jax . Array , heTable : jax . Array , faceTable : jax . Array , width : float , height : float , vertTable_target : jax . Array , heTable_target : jax . Array , faceTable_target : jax . Array , _selected_verts : jax . Array | None = None , _selected_hes : jax . Array | None = None , _selected_faces : jax . Array | None = None , _image_target : jax . Array | None = None ) -> jax . Array :
+
+
View Source
+
+
+
+ 705 @partial ( jit , static_argnums = ( 3 , 4 ))
+706 def cost_tem_halfedge (
+707 vertTable : Array ,
+708 heTable : Array ,
+709 faceTable : Array ,
+710 width : float ,
+711 height : float ,
+712 vertTable_target : Array ,
+713 heTable_target : Array ,
+714 faceTable_target : Array ,
+715 _selected_verts : Array | None = None ,
+716 _selected_hes : Array | None = None ,
+717 _selected_faces : Array | None = None ,
+718 _image_target : Array | None = None ,
+719 ) -> Array :
+720 """TEM-inspired loss using half-edge dual graph (cells)."""
+721
+722 # ---------- helpers ----------
+723 def pairwise_periodic_distances ( x : Array , y : Array ) -> Array :
+724 dx = x [:, None , 0 ] - y [ None , :, 0 ]
+725 dy = x [:, None , 1 ] - y [ None , :, 1 ]
+726
+727 dx = jnp . minimum ( jnp . abs ( dx ), width - jnp . abs ( dx ))
+728 dy = jnp . minimum ( jnp . abs ( dy ), height - jnp . abs ( dy ))
+729
+730 return jnp . sqrt ( dx ** 2 + dy ** 2 + 1e-12 )
+731
+732 def sinkhorn ( a : Array , b : Array , C : Array , eps : float = 5e-2 , n_iters : int = 50 ) -> Array :
+733 K = jnp . exp ( - C / eps )
+734 u = jnp . ones_like ( a )
+735 v = jnp . ones_like ( b )
+736
+737 def body ( _ : int , state : tuple [ Array , Array ]) -> tuple [ Array , Array ]:
+738 u , v = state
+739 u = a / ( K @ v + 1e-12 )
+740 v = b / ( K . T @ u + 1e-12 )
+741 return ( u , v )
+742
+743 u , v = fori_loop ( 0 , n_iters , body , ( u , v ))
+744 return jnp . outer ( u , v ) * K
+745
+746 # ---------- dual graph ----------
+747 def build_dual_adj ( heTable : Array , n_faces : int ) -> Array :
+748 face = heTable [:, 5 ] . astype ( jnp . int32 )
+749 twin = heTable [:, 2 ] . astype ( jnp . int32 )
+750
+751 face_twin = face [ twin ]
+752
+753 valid = face != face_twin
+754 keep = valid & ( face < face_twin )
+755
+756 weight = keep . astype ( jnp . float32 )
+757
+758 A = jnp . zeros (( n_faces , n_faces ))
+759
+760 A = A . at [ face , face_twin ] . add ( weight )
+761 A = A . at [ face_twin , face ] . add ( weight )
+762
+763 return A
+764
+765 def structure_matrix ( A : Array ) -> Array :
+766 # A2 = A @ A
+767 # return 2.0 - A - 0.5 * A2
+768 return 2.0 - A
+769
+770 # ---------- centroids ----------
+771 # faceTable stores a half-edge index → get its source vertex
+772 he_idx = faceTable [:, 0 ] . astype ( jnp . int32 )
+773
+774 X = vertTable [ heTable [ he_idx , 3 ] . astype ( jnp . int32 ), : 2 ]
+775 Y = vertTable_target [ heTable_target [ faceTable_target [:, 0 ] . astype ( jnp . int32 ), 3 ] . astype ( jnp . int32 ), : 2 ]
+776
+777 N = X . shape [ 0 ]
+778 M = Y . shape [ 0 ]
+779
+780 a = jnp . ones ( N ) / N
+781 b = jnp . ones ( M ) / M
+782
+783 # ---------- costs ----------
+784 C_geom = pairwise_periodic_distances ( X , Y )
+785
+786 A1 = build_dual_adj ( heTable , N )
+787 A2 = build_dual_adj ( heTable_target , M )
+788
+789 S1 = structure_matrix ( A1 )
+790 S2 = structure_matrix ( A2 )
+791
+792 # structure comparison (non-periodic, abstract space)
+793 diff = S1 [:, None , :] - S2 [ None , :, :]
+794 C_struct = jnp . sqrt ( jnp . sum ( diff ** 2 , axis =- 1 ) + 1e-12 )
+795
+796 alpha = 0.5
+797 C_total = alpha * C_geom + ( 1.0 - alpha ) * C_struct
+798
+799 gamma = sinkhorn ( a , b , C_total )
+800
+801 return jnp . sum ( gamma * C_total )
+
+
+
+ TEM-inspired loss using half-edge dual graph (cells).
+
+
+
+
+
+
+
+
@partial(jit, static_argnums=(3, 4))
+
+
def
+
cost_v2v_ias ( vertTable : jax . Array , heTable : jax . Array , faceTable : jax . Array , width : float , height : float , vertTable_target : jax . Array , heTable_target : jax . Array , faceTable_target : jax . Array , _selected_verts : jax . Array | None = None , _selected_hes : jax . Array | None = None , _selected_faces : jax . Array | None = None , _image_target : jax . Array | None = None ) -> jax . Array :
+
+
View Source
+
+
+
+ 849 @partial ( jit , static_argnums = ( 3 , 4 ))
+850 def cost_v2v_ias (
+851 vertTable : Array ,
+852 heTable : Array ,
+853 faceTable : Array ,
+854 width : float ,
+855 height : float ,
+856 vertTable_target : Array ,
+857 heTable_target : Array ,
+858 faceTable_target : Array ,
+859 _selected_verts : Array | None = None ,
+860 _selected_hes : Array | None = None ,
+861 _selected_faces : Array | None = None ,
+862 _image_target : Array | None = None ,
+863 ) -> Array :
+864 """Mix of cost_v2v and cost_IAS (with weight 0.6 and 0.4)."""
+865 return 0.6 * cost_v2v (
+866 vertTable ,
+867 heTable ,
+868 faceTable ,
+869 width ,
+870 height ,
+871 vertTable_target ,
+872 heTable_target ,
+873 faceTable_target ,
+874 selected_verts = None ,
+875 selected_hes = None ,
+876 selected_faces = None ,
+877 _image_target = None ,
+878 ) + 0.4 * cost_IAS (
+879 vertTable ,
+880 heTable ,
+881 faceTable ,
+882 width ,
+883 height ,
+884 vertTable_target ,
+885 heTable_target ,
+886 faceTable_target ,
+887 selected_verts = None ,
+888 selected_hes = None ,
+889 selected_faces = None ,
+890 image_target = None ,
+891 )
+
+
+
+ Mix of cost_v2v and cost_IAS (with weight 0.6 and 0.4).
+
+
+
+
+
+
+
+
@partial(jit, static_argnums=(3, 4))
+
+
def
+
cost_v2v_tem ( vertTable : jax . Array , heTable : jax . Array , faceTable : jax . Array , width : float , height : float , vertTable_target : jax . Array , heTable_target : jax . Array , faceTable_target : jax . Array , _selected_verts : jax . Array | None = None , _selected_hes : jax . Array | None = None , _selected_faces : jax . Array | None = None , _image_target : jax . Array | None = None ) -> jax . Array :
+
+
View Source
+
+
+
+ 804 @partial ( jit , static_argnums = ( 3 , 4 ))
+805 def cost_v2v_tem (
+806 vertTable : Array ,
+807 heTable : Array ,
+808 faceTable : Array ,
+809 width : float ,
+810 height : float ,
+811 vertTable_target : Array ,
+812 heTable_target : Array ,
+813 faceTable_target : Array ,
+814 _selected_verts : Array | None = None ,
+815 _selected_hes : Array | None = None ,
+816 _selected_faces : Array | None = None ,
+817 _image_target : Array | None = None ,
+818 ) -> Array :
+819 """Mix of cost_v2v and cost_tem_halfedge (with respective weights 0.99 and 0.01)."""
+820 return 0.99 * cost_v2v (
+821 vertTable ,
+822 heTable ,
+823 faceTable ,
+824 width ,
+825 height ,
+826 vertTable_target ,
+827 heTable_target ,
+828 faceTable_target ,
+829 selected_verts = None ,
+830 selected_hes = None ,
+831 selected_faces = None ,
+832 image_target = None ,
+833 ) + 0.01 * cost_tem_halfedge (
+834 vertTable ,
+835 heTable ,
+836 faceTable ,
+837 width ,
+838 height ,
+839 vertTable_target ,
+840 heTable_target ,
+841 faceTable_target ,
+842 selected_verts = None ,
+843 selected_hes = None ,
+844 selected_faces = None ,
+845 image_target = None ,
+846 )
+
+
+
+ Mix of cost_v2v and cost_tem_halfedge (with respective weights 0.99 and 0.01).
+
+
+
+
+
+
+
+
@jit
+
+
def
+
cost_ratio ( vertTable : jax . Array , _angTable : jax . Array | None = None , _heTable : jax . Array | None = None , _faceTable : jax . Array | None = None , _vertTable_target : jax . Array | None = None , _angTable_target : jax . Array | None = None , _heTable_target : jax . Array | None = None , _faceTable_target : jax . Array | None = None , _selected_verts : jax . Array | None = None , _selected_hes : jax . Array | None = None , _selected_faces : jax . Array | None = None , _image_target : jax . Array | None = None ) -> jax . Array :
+
+
View Source
+
+
+
+ 380 @jit
+381 def cost_ratio (
+382 vertTable : Array ,
+383 _angTable : Array | None = None ,
+384 _heTable : Array | None = None ,
+385 _faceTable : Array | None = None ,
+386 _vertTable_target : Array | None = None ,
+387 _angTable_target : Array | None = None ,
+388 _heTable_target : Array | None = None ,
+389 _faceTable_target : Array | None = None ,
+390 _selected_verts : Array | None = None ,
+391 _selected_hes : Array | None = None ,
+392 _selected_faces : Array | None = None ,
+393 _image_target : Array | None = None ,
+394 ) -> Array :
+395 """Cost that nudges a `BoundedMesh` to elongate along one axis while narrowing along the orthogonal axis."""
+396 # Compute pairwise squared distances by broadcasting
+397 # diff shape: (n, n, d)
+398 diff = vertTable [:, None , :] - vertTable [ None , :, :]
+399 sq_dists = jnp . sum ( diff ** 2 , axis =- 1 ) # (n, n)
+400 distances = jnp . sqrt ( sq_dists + 1e-12 ) # small eps for numerical stability
+401
+402 # For each row, get index of farthest point
+403 max_distances = jnp . max ( distances , axis = 1 )
+404 max_idx_1 = jnp . argmax ( max_distances ) # index of row with largest max distance
+405 long_axis = max_distances [ max_idx_1 ] # longest distance (already excludes diagonal)
+406 max_idx_2 = jnp . argmax ( distances [ max_idx_1 ]) # the farthest point from max_idx_1
+407
+408 max_point_1 = vertTable [ max_idx_1 ]
+409 max_point_2 = vertTable [ max_idx_2 ]
+410 long_axis_vector = max_point_2 - max_point_1
+411 long_axis_vector = long_axis_vector / ( jnp . linalg . norm ( long_axis_vector ) + 1e-12 )
+412
+413 # Short axis (perpendicular)
+414 short_axis_vector = jnp . array ([ long_axis_vector [ 1 ], - long_axis_vector [ 0 ]])
+415
+416 # Project points onto short axis: signed distances
+417 # If vertTable is (n,2) this works. If higher dim, modify accordingly.
+418 signed_distances_to_long_axis = jnp . dot ( vertTable - max_point_1 , short_axis_vector )
+419
+420 short_axis = jnp . max ( signed_distances_to_long_axis ) - jnp . min ( signed_distances_to_long_axis )
+421
+422 # Numerically stabilize division
+423 ratio = long_axis / ( short_axis + 1e-12 )
+424 return ( ratio - TARGET_RATIO ) ** 2
+
+
+
+ Cost that nudges a BoundedMesh to elongate along one axis while narrowing along the orthogonal axis.
+
+
+
+
+
+
+
+
@jit
+
+
def
+
cost_checkerboard ( vertTable : jax . Array , _angTable : jax . Array , heTable : jax . Array , faceTable : jax . Array , _vertTable_target : jax . Array | None = None , _angTable_target : jax . Array | None = None , _heTable_target : jax . Array | None = None , _faceTable_target : jax . Array | None = None , _selected_verts : jax . Array | None = None , _selected_hes : jax . Array | None = None , _selected_faces : jax . Array | None = None , _image_target : jax . Array | None = None ) -> jax . Array :
+
+
View Source
+
+
+
+ 427 @jit
+428 def cost_checkerboard (
+429 vertTable : Array ,
+430 _angTable : Array ,
+431 heTable : Array ,
+432 faceTable : Array ,
+433 _vertTable_target : Array | None = None ,
+434 _angTable_target : Array | None = None ,
+435 _heTable_target : Array | None = None ,
+436 _faceTable_target : Array | None = None ,
+437 _selected_verts : Array | None = None ,
+438 _selected_hes : Array | None = None ,
+439 _selected_faces : Array | None = None ,
+440 _image_target : Array | None = None ,
+441 ) -> Array :
+442 """Cost that nudges a `BoundedMesh` with different fated cells to avoid having neighboring cells with same fate.
+443
+444 This leads to a checkerboard pattern when there is 2 fates.
+445 """
+446
+447 def body_fun ( i : int , current_len : Array ) -> Array :
+448 idx = 2 * i
+449 he_twin = heTable [ idx , 2 ]
+450 he_face = heTable [ idx , 7 ]
+451 he_fate = faceTable [ he_face , 1 ]
+452 he_face_twin = heTable [ he_twin , 7 ]
+453 he_fate_twin = faceTable [ he_face_twin , 1 ]
+454 v_source = heTable [ idx , 3 ] - 2
+455 v_target = heTable [ idx , 4 ] - 2
+456 pos_source = vertTable [ v_source ]
+457 pos_target = vertTable [ v_target ]
+458 return current_len + jnp . where (
+459 jnp . logical_and ( he_fate == he_fate_twin , heTable [ idx , 3 ] > 0 ), jnp . sum (( pos_target - pos_source ) ** 2 ), 0.0
+460 )
+461
+462 n_edges = heTable . shape [ 0 ] // 2
+463 return fori_loop ( 0 , n_edges , body_fun , 0.0 )
+
+
+
+ Cost that nudges a BoundedMesh with different fated cells to avoid having neighboring cells with same fate.
+
+
This leads to a checkerboard pattern when there is 2 fates.
+
+
+
+
+
+
+
+
+ def
+ energy_shape_factor_hetero ( vertTable : jax . Array , heTable : jax . Array , faceTable : jax . Array , width : float , height : float , selected_faces : jax . Array , face_params : jax . Array ) -> jax . Array :
+
+ View Source
+
+
+
+ 75 def energy_shape_factor_hetero (
+76 vertTable : Array ,
+77 heTable : Array ,
+78 faceTable : Array ,
+79 width : float ,
+80 height : float ,
+81 selected_faces : Array ,
+82 face_params : Array ,
+83 ) -> Array :
+84 """E1 energy where the shape factor depends on the cell."""
+85
+86 def mapped_fn ( face : Array , param : Array ) -> Array :
+87 return _cell_energy ( face , param , vertTable , heTable , faceTable , width , height )
+88
+89 cell_energies = vmap ( mapped_fn )( selected_faces , face_params [ selected_faces ])
+90 # cell_energies = vmap(mapped_fn)(jnp.arange(len(faceTable)), face_params)
+91 return jnp . sum ( cell_energies )
+
+
+
+ E1 energy where the shape factor depends on the cell.
+
+
+
+
+
+
+
+
+ def
+ energy_shape_factor_homo ( vertTable : jax . Array , heTable : jax . Array , faceTable : jax . Array , width : float , height : float , face_params : jax . Array ) -> jax . Array :
+
+ View Source
+
+
+
+ 57 def energy_shape_factor_homo (
+58 vertTable : Array ,
+59 heTable : Array ,
+60 faceTable : Array ,
+61 width : float ,
+62 height : float ,
+63 face_params : Array ,
+64 ) -> Array :
+65 """E1 energy where the shape factor is uniform (give only one face_params, it will be broadcasted)."""
+66
+67 def mapped_fn ( face : Array , param : Array ) -> Array :
+68 return _cell_energy ( face , param , vertTable , heTable , faceTable , width , height )
+69
+70 face_params_broadcasted = jnp . broadcast_to ( face_params , ( len ( faceTable ), * face_params . shape [ 1 :]))
+71 cell_energies = vmap ( mapped_fn )( jnp . arange ( len ( faceTable )), face_params_broadcasted )
+72 return jnp . sum ( cell_energies )
+
+
+
+ E1 energy where the shape factor is uniform (give only one face_params, it will be broadcasted).
+
+
+
+
+
+
+
+
+ def
+ energy_line_tensions ( vertTable : jax . Array , heTable : jax . Array , faceTable : jax . Array , width : float , height : float , he_params : jax . Array , face_params : jax . Array ) -> jax . Array :
+
+ View Source
+
+
+
+ 110 def energy_line_tensions (
+111 vertTable : Array ,
+112 heTable : Array ,
+113 faceTable : Array ,
+114 width : float ,
+115 height : float ,
+116 he_params : Array ,
+117 face_params : Array ,
+118 ) -> Array :
+119 """E2 energy for PBC meshes, elastic penalty on cell areas and line tension term weighted by edge lengths."""
+120 K_areas = 20
+121
+122 def mapped_areas_part ( face : Array , face_param : Array ) -> Array :
+123 return _area_part ( face , face_param , vertTable , heTable , faceTable , width , height )
+124
+125 def mapped_hedges_part ( he : Array , he_param : Array ) -> Array :
+126 return _hedge_part ( he , he_param , vertTable , heTable , faceTable , width , height )
+127
+128 areas_part = vmap ( mapped_areas_part )( jnp . arange ( len ( faceTable )), face_params )
+129 hedges_part = vmap ( mapped_hedges_part )( jnp . arange ( len ( heTable )), he_params )
+130 # areas_part = vmap(mapped_areas_part)(selected_faces, face_params[selected_faces])
+131 # hedges_part = vmap(mapped_hedges_part)(selected_hes, he_params[selected_hes])
+132 return jnp . sum ( hedges_part ) + ( 0.5 * K_areas ) * jnp . sum ( areas_part )
+
+
+
+ E2 energy for PBC meshes, elastic penalty on cell areas and line tension term weighted by edge lengths.
+
+
+
+
+
+
+
+
@jit
+
+
def
+
energy_line_tensions_bounded ( vertTable : jax . Array , angTable : jax . Array , heTable : jax . Array , faceTable : jax . Array , _selected_verts : jax . Array | None , _selected_hes : jax . Array | None , _selected_faces : jax . Array | None , _vert_params : jax . Array , he_params : jax . Array , _face_params : jax . Array ) -> jax . Array :
+
+
View Source
+
+
+
+ 159 @jit
+160 def energy_line_tensions_bounded (
+161 vertTable : Array ,
+162 angTable : Array ,
+163 heTable : Array ,
+164 faceTable : Array ,
+165 _selected_verts : Array | None ,
+166 _selected_hes : Array | None ,
+167 _selected_faces : Array | None ,
+168 _vert_params : Array ,
+169 he_params : Array ,
+170 _face_params : Array ,
+171 ) -> Array :
+172 """E2 energy for bounded meshes, elastic penalty on cell areas and line tension term weighted by edge lengths."""
+173 num_faces = faceTable . shape [ 0 ]
+174 faces = jnp . arange ( num_faces )
+175 num_edges = angTable . size
+176 num_half_edges = num_edges * 2
+177 unique_edges = jnp . arange ( num_edges ) * 2
+178 edges = jnp . arange ( num_half_edges )
+179 vertTable = jnp . vstack ([ jnp . array ([[ 0.0 , 0.0 ], [ 1.0 , 1.0 ]]), vertTable ])
+180 angTable = jnp . repeat ( angTable , 2 )
+181 he_params = jax . nn . sigmoid ( he_params ) + 1
+182
+183 def mapped_fn_area ( face : Array ) -> Array :
+184 return _cell_area_energy ( face , vertTable , angTable , heTable , faceTable )
+185
+186 cell_area_energies = jnp . sum ( vmap ( mapped_fn_area )( faces ))
+187
+188 def mapped_fn_inner ( edge : Array , tension : Array ) -> Array :
+189 return _inner_edge_energy ( edge , tension , vertTable , heTable )
+190
+191 inner_edge_energies = jnp . sum ( vmap ( mapped_fn_inner )( unique_edges , he_params ))
+192
+193 def mapped_fn_surface ( edge : Array , tension : Array ) -> Array :
+194 return _surface_edge_energy ( edge , tension , vertTable , angTable , heTable )
+195
+196 surface_edge_energies = jnp . sum ( vmap ( mapped_fn_surface )( edges , jnp . repeat ( he_params , 2 )))
+197 return 20 * cell_area_energies + inner_edge_energies + surface_edge_energies
+
+
+
+ E2 energy for bounded meshes, elastic penalty on cell areas and line tension term weighted by edge lengths.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/image.tif b/examples/image.tif
similarity index 100%
rename from docs/image.tif
rename to examples/image.tif
diff --git a/docs/inverse_modelling_example.ipynb b/examples/inverse_modelling_example.ipynb
similarity index 99%
rename from docs/inverse_modelling_example.ipynb
rename to examples/inverse_modelling_example.ipynb
index ac4fe4b..bf7ae10 100644
--- a/docs/inverse_modelling_example.ipynb
+++ b/examples/inverse_modelling_example.ipynb
@@ -2,21 +2,28 @@
"cells": [
{
"cell_type": "markdown",
- "id": "84054881",
+ "id": "13f2b77c",
"metadata": {},
"source": [
"# Inverse Modeling example\n",
"\n",
"We'll show a simple example for inverse modeling using automatic differentiation on a vertex model with periodic boundary conditions.\n",
"\n",
- "First, let's check that VertAX is correctly installed by importing all we'll need for the next parts"
+ "First, let's check that VertAX is correctly installed by importing all we'll need for the next parts\n",
+ "coucou"
]
},
{
"cell_type": "code",
"execution_count": 1,
- "id": "345bbc89",
- "metadata": {},
+ "id": "1b7ac4eb",
+ "metadata": {
+ "collapsed": false,
+ "jupyter": {
+ "outputs_hidden": false
+ },
+ "lines_to_next_cell": 2
+ },
"outputs": [
{
"name": "stderr",
@@ -28,12 +35,12 @@
],
"source": [
"from vertax import PbcBilevelOptimizer, PbcMesh\n",
- "# We'll see how to those important classes.\n"
+ "# We'll see how to those important classes."
]
},
{
"cell_type": "markdown",
- "id": "8bed60a6",
+ "id": "2d955336",
"metadata": {},
"source": [
"Now, let's create our initial mesh. It will have 20 cells, in a square domain."
@@ -42,7 +49,7 @@
{
"cell_type": "code",
"execution_count": 2,
- "id": "9c73e90f",
+ "id": "f67d2428",
"metadata": {},
"outputs": [],
"source": [
@@ -62,7 +69,7 @@
{
"cell_type": "code",
"execution_count": 3,
- "id": "8f21528d",
+ "id": "6ca2dd8c",
"metadata": {},
"outputs": [
{
@@ -93,7 +100,7 @@
},
{
"cell_type": "markdown",
- "id": "7ddaf864",
+ "id": "9666a337",
"metadata": {},
"source": [
"The mesh is associated to parameters on vertices, (half-)edges and faces (cells). The meaning of these parameters is \"given\" by the loss functions.\n",
@@ -104,7 +111,7 @@
{
"cell_type": "code",
"execution_count": 4,
- "id": "9de49e7d",
+ "id": "2a82d406",
"metadata": {},
"outputs": [],
"source": [
@@ -130,7 +137,7 @@
},
{
"cell_type": "markdown",
- "id": "f02b8ac7",
+ "id": "4902f316",
"metadata": {},
"source": [
"It is time to define our custom inner loss function. This energy function will penalize cell areas different than 1 and the length of edges depending on the tension on them."
@@ -139,7 +146,7 @@
{
"cell_type": "code",
"execution_count": 5,
- "id": "f66f352a",
+ "id": "47f3e4c9",
"metadata": {},
"outputs": [],
"source": [
@@ -201,7 +208,7 @@
},
{
"cell_type": "markdown",
- "id": "3090d63f",
+ "id": "aae27e7d",
"metadata": {},
"source": [
"Now we are ready to perform the inner optimization ! This part is done by a PbcBilevelOptimizer, that stores hyper-parameters for the optimizations.\n",
@@ -212,7 +219,7 @@
{
"cell_type": "code",
"execution_count": 6,
- "id": "cf6e18fc",
+ "id": "9d7f09cc",
"metadata": {},
"outputs": [],
"source": [
@@ -220,7 +227,7 @@
"\n",
"bilevel_optimizer = PbcBilevelOptimizer()\n",
"bilevel_optimizer.loss_function_inner = energy # Don't forget to set the loss function !\n",
- "# Note: those are base values so the following can be ommited\n",
+ "# Note: those are base values so the following can be omitted\n",
"bilevel_optimizer.inner_solver = optax.sgd(learning_rate=0.01) # inner solver\n",
"bilevel_optimizer.update_T1 = True # Perform T1 transitions if necessary.\n",
"bilevel_optimizer.min_dist_T1 = 0.005 # Threshold to perform the T1 transitions.\n",
@@ -231,7 +238,7 @@
},
{
"cell_type": "markdown",
- "id": "6ed9524b",
+ "id": "b3d999ae",
"metadata": {},
"source": [
"Let's perform the inner optimization. It's as simple as that:"
@@ -240,11 +247,11 @@
{
"cell_type": "code",
"execution_count": 7,
- "id": "9972d85c",
+ "id": "33d0af5f",
"metadata": {},
"outputs": [],
"source": [
- "# Other paramerters are image_target (for cost_mesh2image), beta (for EP).\n",
+ "# Other parameters are image_target (for cost_mesh2image), beta (for EP).\n",
"loss_history = bilevel_optimizer.inner_optimization(mesh)\n",
"# If you want to select only a subset of vertices, edges, and faces, it's possible:\n",
"# bilevel_optimizer.inner_optimization(mesh,\n",
@@ -257,7 +264,7 @@
{
"cell_type": "code",
"execution_count": 8,
- "id": "4308eaef",
+ "id": "efa086f1",
"metadata": {},
"outputs": [
{
@@ -277,7 +284,7 @@
},
{
"cell_type": "markdown",
- "id": "2a7048cc",
+ "id": "7b7fe595",
"metadata": {},
"source": [
"The inner optimization nudged the mesh to minimize the length of edges with high tension while keeping the area of each face close to 1.\n",
@@ -292,7 +299,7 @@
{
"cell_type": "code",
"execution_count": 9,
- "id": "b5b0ff6e",
+ "id": "54bb4d68",
"metadata": {},
"outputs": [],
"source": [
@@ -314,7 +321,7 @@
{
"cell_type": "code",
"execution_count": 10,
- "id": "dbc9b248",
+ "id": "5f9422b5",
"metadata": {},
"outputs": [
{
@@ -334,7 +341,7 @@
},
{
"cell_type": "markdown",
- "id": "45a64071",
+ "id": "470b7566",
"metadata": {},
"source": [
"OK now we obtained our target vertices, edges and faces, let's store them in our mesh : they become the mesh target."
@@ -343,7 +350,7 @@
{
"cell_type": "code",
"execution_count": 11,
- "id": "aeecde26",
+ "id": "6ce435cb",
"metadata": {},
"outputs": [],
"source": [
@@ -351,7 +358,7 @@
"bilevel_optimizer.edges_target = target.edges.copy()\n",
"bilevel_optimizer.faces_target = target.faces.copy()\n",
"# We can discard the target mesh now because it is of no use.\n",
- "del target\n",
+ "# del target\n",
"\n",
"energy_target = get_energy_function(he_params_reference_target)\n",
"bilevel_optimizer.loss_function_inner = energy_target # Update the inner loss function"
@@ -359,7 +366,7 @@
},
{
"cell_type": "markdown",
- "id": "f619f041",
+ "id": "15d02844",
"metadata": {},
"source": [
"In addition to the previous parameters for the inner optimization, the following parameters are used during the bilevel optimization process:"
@@ -368,7 +375,7 @@
{
"cell_type": "code",
"execution_count": 12,
- "id": "f6520722",
+ "id": "a360cf63",
"metadata": {},
"outputs": [],
"source": [
@@ -379,14 +386,14 @@
"\n",
"from vertax import BilevelOptimizationMethod # An enumeration of the methods you can use\n",
"\n",
- "# Once again this part can be ommited because those are the base value for these parameters\n",
+ "# Once again this part can be omitted because those are the base value for these parameters\n",
"bilevel_optimizer.outer_solver = optax.adam(learning_rate=0.0001, nesterov=True) # outer solver\n",
"bilevel_optimizer.bilevel_optimization_method = BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION"
]
},
{
"cell_type": "markdown",
- "id": "78008e0c",
+ "id": "8d27e00d",
"metadata": {},
"source": [
"Let's run our bilevel optimization process. The first epoch is always a bit long but the next ones will be quick.\n",
@@ -398,36 +405,47 @@
},
{
"cell_type": "code",
- "execution_count": 13,
- "id": "9a463668",
+ "execution_count": 39,
+ "id": "efe743c7",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
- "epoch: 0/10, actual cost is 0.00399027019739151\n",
- "epoch 0 took 6.920719959000053 seconds.\n",
- "epoch: 1/10, actual cost is 0.003931049257516861\n",
- "epoch 1 took 1.240284757000154 seconds.\n",
- "epoch: 2/10, actual cost is 0.003896141890436411\n",
- "epoch 2 took 1.2602369420001196 seconds.\n",
- "epoch: 3/10, actual cost is 0.003853569971397519\n",
- "epoch 3 took 1.2731893489999493 seconds.\n",
- "epoch: 4/10, actual cost is 0.0038316622376441956\n",
- "epoch 4 took 1.2410099319995425 seconds.\n",
- "epoch: 5/10, actual cost is 0.003800261067226529\n",
- "epoch 5 took 1.2721578809996572 seconds.\n",
- "epoch: 6/10, actual cost is 0.0037877082359045744\n",
- "epoch 6 took 1.2755202250000366 seconds.\n",
- "epoch: 7/10, actual cost is 0.0037637564819306135\n",
- "epoch 7 took 1.2615269740008443 seconds.\n",
- "epoch: 8/10, actual cost is 0.0037574756424874067\n",
- "epoch 8 took 1.259819206999964 seconds.\n",
- "epoch: 9/10, actual cost is 0.003738644067198038\n",
- "epoch 9 took 1.2245857420002721 seconds.\n",
- "epoch: 10/10, actual cost is 0.0037368000485002995\n",
- "epoch 10 took 1.2501073559997167 seconds.\n"
+ "epoch: 0/10, actual cost is 0.0037211880553513765\n",
+ "epoch 0 took 1.2170525080000516 seconds.\n",
+ "-0.009380522184073925\n",
+ "epoch: 1/10, actual cost is 0.003721958724781871\n",
+ "epoch 1 took 1.1607601240002623 seconds.\n",
+ "-0.008781428448855877\n",
+ "epoch: 2/10, actual cost is 0.0037086878437548876\n",
+ "epoch 2 took 1.1663910829997803 seconds.\n",
+ "-0.008116534911096096\n",
+ "epoch: 3/10, actual cost is 0.003711005672812462\n",
+ "epoch 3 took 1.1220879219999915 seconds.\n",
+ "-0.007402104791253805\n",
+ "epoch: 4/10, actual cost is 0.0036993816029280424\n",
+ "epoch 4 took 1.1419530690000101 seconds.\n",
+ "-0.006774387322366238\n",
+ "epoch: 5/10, actual cost is 0.0037029192317277193\n",
+ "epoch 5 took 1.1655332840000483 seconds.\n",
+ "-0.0061574820429086685\n",
+ "epoch: 6/10, actual cost is 0.0036917426623404026\n",
+ "epoch 6 took 1.1711282279998159 seconds.\n",
+ "-0.0055525656789541245\n",
+ "epoch: 7/10, actual cost is 0.003695834195241332\n",
+ "epoch 7 took 1.146710374000122 seconds.\n",
+ "-0.004930838942527771\n",
+ "epoch: 8/10, actual cost is 0.003685095114633441\n",
+ "epoch 8 took 1.1108373649999521 seconds.\n",
+ "-0.0042711300775408745\n",
+ "epoch: 9/10, actual cost is 0.0036892385687679052\n",
+ "epoch 9 took 1.291769063999709 seconds.\n",
+ "-0.0035925169941037893\n",
+ "epoch: 10/10, actual cost is 0.003678437788039446\n",
+ "epoch 10 took 1.1649115280001752 seconds.\n",
+ "-0.002958990866318345\n"
]
}
],
@@ -455,12 +473,14 @@
" print(f\"epoch: {epoch}/{nb_epochs}, actual cost is {cost}\")\n",
"\n",
" loss_history = bilevel_optimizer.bilevel_optimization(mesh)\n",
- " print(f\"epoch {epoch} took {perf_counter() - t_start} seconds.\")"
+ " print(f\"epoch {epoch} took {perf_counter() - t_start} seconds.\")\n",
+ " pearson_corr = float(jnp.corrcoef(mesh.edges_params, target.edges_params)[0, 1])\n",
+ " print(pearson_corr)"
]
},
{
"cell_type": "markdown",
- "id": "f0e4752d",
+ "id": "ea837f4c",
"metadata": {},
"source": [
"Just to check that the process is working, let's do a basic plot of the cost in function of the epoch."
@@ -469,7 +489,7 @@
{
"cell_type": "code",
"execution_count": 14,
- "id": "d5f6ece4",
+ "id": "9a8e62c0",
"metadata": {},
"outputs": [
{
@@ -492,7 +512,7 @@
},
{
"cell_type": "markdown",
- "id": "546c53b1",
+ "id": "90477738",
"metadata": {},
"source": [
"Of course, a higher number of epochs is necessary for the cost function to stabilize and for a result to be visible in the mesh. If we plot the mesh after a sufficient number of epochs, we'll see that the vertices moved to match the best it can the target while respecting tensions and area constraints."
@@ -501,7 +521,7 @@
{
"cell_type": "code",
"execution_count": 15,
- "id": "99a31f74",
+ "id": "0ee24c4d",
"metadata": {},
"outputs": [
{
@@ -521,7 +541,7 @@
},
{
"cell_type": "markdown",
- "id": "83bd2c95",
+ "id": "37cb905f",
"metadata": {},
"source": [
"We can also plot the tensions and areas:"
@@ -530,7 +550,7 @@
{
"cell_type": "code",
"execution_count": 16,
- "id": "e1d89e64",
+ "id": "f33a4402",
"metadata": {},
"outputs": [
{
@@ -555,11 +575,59 @@
},
{
"cell_type": "code",
- "execution_count": null,
- "id": "1bf3f553-62ca-476c-a780-ddaa1a8fe372",
+ "execution_count": 36,
+ "id": "1e3e63f9",
"metadata": {},
- "outputs": [],
- "source": []
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "nan\n",
+ "[1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]\n",
+ "[1.0005894 0.999558 1.0007367 0.99882126 0.9992633 0.9986739\n",
+ " 0.99911594 0.9997053 1.0014734 1.000442 1.0016208 0.9994106\n",
+ " 1.000442 1. 1.0005894 0.9997053 1.000884 0.99985266\n",
+ " 0.99985266 0.99882126]\n",
+ "20\n",
+ "20\n",
+ "[[ 1. nan]\n",
+ " [nan nan]]\n",
+ "1.0\n",
+ "[[7.2733764e-07 0.0000000e+00]\n",
+ " [0.0000000e+00 0.0000000e+00]]\n"
+ ]
+ }
+ ],
+ "source": [
+ "pearson_corr = float(jnp.corrcoef(mesh.faces_params, target.faces_params)[0, 1])\n",
+ "print(pearson_corr)\n",
+ "print(target.faces_params)\n",
+ "print(mesh.faces_params)\n",
+ "print(len(mesh.faces_params))\n",
+ "print(len(target.faces_params))\n",
+ "print((jnp.corrcoef(mesh.faces_params, target.faces_params)))\n",
+ "print((jnp.cov(mesh.faces_params, target.faces_params)))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 38,
+ "id": "29dc679f-8bcd-428f-a3f7-305e1f3af68c",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "-0.009977000765502453\n"
+ ]
+ }
+ ],
+ "source": [
+ "pearson_corr = float(jnp.corrcoef(mesh.edges_params, target.edges_params)[0, 1])\n",
+ "print(pearson_corr)"
+ ]
}
],
"metadata": {
diff --git a/docs/inverse_modelling_example_bounded.ipynb b/examples/inverse_modelling_example_bounded.ipynb
similarity index 100%
rename from docs/inverse_modelling_example_bounded.ipynb
rename to examples/inverse_modelling_example_bounded.ipynb
diff --git a/docs/mask.tif b/examples/mask.tif
similarity index 100%
rename from docs/mask.tif
rename to examples/mask.tif
diff --git a/docs/pbc_from_image.ipynb b/examples/pbc_from_image.ipynb
similarity index 99%
rename from docs/pbc_from_image.ipynb
rename to examples/pbc_from_image.ipynb
index 268d149..50375df 100644
--- a/docs/pbc_from_image.ipynb
+++ b/examples/pbc_from_image.ipynb
@@ -174,7 +174,7 @@
],
"metadata": {
"kernelspec": {
- "display_name": ".venv",
+ "display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
@@ -188,7 +188,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.13.11"
+ "version": "3.14.3"
}
},
"nbformat": 4,
diff --git a/figures/benchmarks.png b/figures/benchmarks.png
new file mode 100644
index 0000000..868de14
Binary files /dev/null and b/figures/benchmarks.png differ
diff --git a/figures/concept.png b/figures/concept.png
new file mode 100644
index 0000000..0670163
Binary files /dev/null and b/figures/concept.png differ
diff --git a/figures/periodic_bounded.png b/figures/periodic_bounded.png
new file mode 100644
index 0000000..54e01d1
Binary files /dev/null and b/figures/periodic_bounded.png differ
diff --git a/figures/vertax_text.png b/figures/vertax_text.png
new file mode 100644
index 0000000..bcf35fc
Binary files /dev/null and b/figures/vertax_text.png differ
diff --git a/pyproject.toml b/pyproject.toml
index 0f8c625..86ea895 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -149,6 +149,7 @@ markers = ["long: marks tests as slow (deselect with '-m \"not slow\"')"]
[dependency-groups]
dev = [
+ "pdoc>=16.0.0",
"pytest>=8.4.2",
"scalene>=1.5.55",
]
diff --git a/src/vertax/__init__.py b/src/vertax/__init__.py
index 44f8ec9..97e830b 100644
--- a/src/vertax/__init__.py
+++ b/src/vertax/__init__.py
@@ -1,21 +1,108 @@
-"""Main VertAX module."""
+"""A differentiable JAX-based framework for vertex modeling and inverse design of epithelial tissues.
+Epithelial tissues dynamically reshape through local mechanical interactions among cells.
+Understanding, inferring, and designing these mechanics is a central challenge in developmental biology and biophysics.
+**VertAX** is a computational framework built to address this challenge.
+
+**VertAX** is a **framework for vertex-based modeling**:
+ it represents epithelial tissues as two-dimensional polygonal meshes in which cells are faces, junctions are edges,
+ tricellular contacts are vertices, and mechanical equilibrium is defined by the minimum of a user-specified energy.
+
+ Built on **JAX**, VertAX is designed not only for forward simulation,
+ but also for inverse problems such as parameter inference and tissue design.
+
+In **VertAX**, a `Mesh` represents a vertex model of an epithelial tissue. It is made of faces (cells), edges (interface between cells) and vertices (where 3 cells or more meet).
+
+More specifically, two types of meshes are currently supported:
+- `PbcMesh` have Periodic Boundary Conditions, and are used for bulk tissue dynamics without explicit external boundaries.
+- `BoundedMesh` are designed for finite tissue clusters, with curved free interfaces.
+
+The other first class objects of **VertAX** are called Bilevel Optimizers. They allow to formulate inverse problems as nested optimizations:
+
+$$
+\\begin{aligned}
+\\textbf{Outer problem (learning):} \\quad
+\\theta^{\\ast} &= \\arg\\min_{\\theta} \\mathcal{C}\\left(X^{\\ast}_{\\theta},\\theta\\right)
+&& \\leftarrow \\text{fit data or reach a target} \\\\
+\\end{aligned}
+$$
+$$
+\\begin{aligned}
+\\textbf{Inner problem (physics):} \\quad \\text{s.t.}&
+X^{\\ast}_{\\theta} \\in \\arg\\min_{X} \\mathcal{E}(X,\\theta)
+&& \\leftarrow \\text{compute mechanical equilibrium}
+\\end{aligned}
+$$
+
+
+Here, $X$ denotes the tissue configuration, i.e. the vertex positions of the mesh, and $\\theta$ denotes the model parameters, such as line tensions, target areas, or shape factors.
+
+In other words, VertAX repeatedly solves a mechanical equilibrium problem for a given parameter set $\\theta$, then updates those parameters to better match data or a design objective.
+
+In symmetry with meshes, a base abstract class `_BilevelOptimizer` defines common hyper-parameters and methods for the bilevel optimization, but you need to use the specialized classes:
+- `PbcBilevelOptimizer` for `PbcMesh`,
+- `BoundedBilevelOptimizer` for `BoundedMesh`.
+
+**VertAX** comes with pre-defined energy and cost functions but you can easily define your own functions. See the `examples` folder in the repository to have a typical example on how to use **VertAX**.
+
+Finally, there are plot functions to easily see the results of your experiments. See `plot_mesh` for example.
+
+Users can define their own energy functions for the inner optimization and cost function for the outer optimization, however we provide some basic ones.
+If you use your own make sure to use the exact same signature as we do for these functions, otherwise it won't work. See the cost and energy functions we provide in this documentation.
+""" # noqa: D301, E501
+
+from vertax.bilevelopt.bilevelopt import _BilevelOptimizer
from vertax.bilevelopt.boundedbop import BoundedBilevelOptimizer
from vertax.bilevelopt.pbcbop import PbcBilevelOptimizer
+from vertax.cost import (
+ cost_areas,
+ cost_checkerboard,
+ cost_d_IAS,
+ cost_IAS,
+ cost_mesh2image,
+ cost_ratio,
+ cost_tem_halfedge,
+ cost_v2v,
+ cost_v2v_ias,
+ cost_v2v_tem,
+)
+from vertax.energy import (
+ energy_line_tensions,
+ energy_line_tensions_bounded,
+ energy_shape_factor_hetero,
+ energy_shape_factor_homo,
+)
from vertax.meshes.bounded_mesh import BoundedMesh
+from vertax.meshes.mesh import Mesh
from vertax.meshes.pbc_mesh import PbcMesh
from vertax.meshes.plot import EdgePlot, FacePlot, VertexPlot, get_plot_mesh, plot_mesh
from vertax.method_enum import BilevelOptimizationMethod
-__all__ = [
+__all__ = [ # noqa: RUF022
+ "Mesh",
+ "PbcMesh",
+ "BoundedMesh",
"BilevelOptimizationMethod",
+ "_BilevelOptimizer",
+ "PbcBilevelOptimizer",
"BoundedBilevelOptimizer",
- "BoundedMesh",
- "EdgePlot",
+ "plot_mesh",
+ "get_plot_mesh",
"FacePlot",
- "PbcBilevelOptimizer",
- "PbcMesh",
+ "EdgePlot",
"VertexPlot",
- "get_plot_mesh",
- "plot_mesh",
+ "cost_v2v",
+ "cost_mesh2image",
+ "cost_areas",
+ "cost_IAS",
+ "cost_d_IAS",
+ "cost_tem_halfedge",
+ "cost_v2v_ias",
+ "cost_v2v_tem",
+ "cost_ratio",
+ "cost_checkerboard",
+ "energy_shape_factor_hetero",
+ "energy_shape_factor_homo",
+ "energy_line_tensions",
+ "energy_line_tensions_bounded",
]
diff --git a/src/vertax/bilevelopt/__init__.py b/src/vertax/bilevelopt/__init__.py
index 3e063ab..8b2eb09 100644
--- a/src/vertax/bilevelopt/__init__.py
+++ b/src/vertax/bilevelopt/__init__.py
@@ -1 +1 @@
-"""Bilevel optimization problem classes."""
+"""Bilevel optimization problem classes. Use the class corresponding to your meshes."""
diff --git a/src/vertax/bilevelopt/bilevelopt.py b/src/vertax/bilevelopt/bilevelopt.py
index 20ce2f9..bbad8a1 100644
--- a/src/vertax/bilevelopt/bilevelopt.py
+++ b/src/vertax/bilevelopt/bilevelopt.py
@@ -20,22 +20,38 @@
class _BilevelOptimizer:
- """Abstract class for Bi-level optimizers."""
+ """Abstract base class for Bi-level optimizers.
+
+ Use the specialezed corresponding ones:
+ - to optimize a `PbcMesh` use a `PbcBilevelOptimizer`,
+ - to optimize a `BoundedMesh` use a `BoundedBilevelOptimizer`.
+ """
def __init__(self) -> None:
"""Initialize shared parameters and hyper-parameters between Bi-level optimizers."""
- self.custom_metrics: dict[str, tuple[Callable[[Any, Any], float], list[float], list[float]]] = {}
self.bilevel_optimization_method: BilevelOptimizationMethod = BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION
+ """Which bilevel optimization method to use. Defaults to Equilibrium Propagation."""
self.inner_solver: optax.GradientTransformation = optax.sgd(learning_rate=0.01)
+ """Which inner solver to use. Defaults to `optax.sgd(learning_rate=0.01)`."""
self.outer_solver: optax.GradientTransformation = optax.adam(learning_rate=0.0001, nesterov=True)
+ """Which outer solver to use. Defaults to `optax.adam(learning_rate=0.0001, nesterov=True)`"""
self.loss_function_inner: Callable | None = None
+ """Define your inner loss function."""
self.loss_function_outer: Callable | None = None
+ """Define your outer loss function."""
+ self.custom_metrics: dict[str, tuple[Callable[[Any, Any], float], list[float], list[float]]] = {}
+ """Define custom metrics that can be saved during optimization.
+ The function must take two arguments : a mesh and a bilevel optimizer ; and return a float."""
self.max_nb_iterations: int = 1000
+ """Maximum number of iterations during an optimization step."""
self.tolerance: float = 1e-4
+ """Below this level, we consider that the loss is stagnating."""
self.patience: int = 5
+ """Maximum number of consecutive stagnating loss before we stop."""
self.min_dist_T1: float = 0.005
+ """Threshold to perform T1 transitions."""
self._update_T1: bool = False
self._update_T1_func: Callable | None = None # value set by _set_update_T1_func
@@ -44,20 +60,23 @@ def __init__(self) -> None:
self._outer_opt_func: Callable[[Mesh, Array | None, Array | None, Array | None], None] | None = None
self.update_T1 = True # Force the setting of update T1 func
+ """Perform T1 transitions if necessary."""
# Targets
self.vertices_target = jnp.array([])
+ """Vertices table of a target mesh if any."""
self.edges_target = jnp.array([])
+ """Edges table of a target mesh if any."""
self.faces_target = jnp.array([])
+ """Faces table of a target mesh if any."""
# Those attributes are not always used (depends on the bilevel_optimization_method)
self.image_target: Array = jnp.array([])
+ """Image target if any."""
self.beta = 0.01
+ """β parameter used for Equilibrium Propagation."""
def _set_update_T1_func(self, b: bool) -> None: # noqa: N802
- """Set the _update_T1_func callable with respect to whether it is needed or not.
-
- Must be implemented by child classes.
- """
+ """Set the _update_T1_func callable with respect to whether it is needed or not."""
raise NotImplementedError
@property
@@ -158,7 +177,7 @@ def bilevel_optimization(
only_on_edges: None | list[int] = None,
only_on_faces: None | list[int] = None,
) -> list[float]:
- """Optimize the mesh for the loss function given.
+ """Optimize the mesh for the loss functions given.
Args:
mesh (Mesh): The mesh to act on.
diff --git a/src/vertax/bilevelopt/boundedbop.py b/src/vertax/bilevelopt/boundedbop.py
index 6c7d25a..5b4c581 100644
--- a/src/vertax/bilevelopt/boundedbop.py
+++ b/src/vertax/bilevelopt/boundedbop.py
@@ -20,10 +20,13 @@
class BoundedBilevelOptimizer(_BilevelOptimizer):
- """Bi-level optimizer for periodic boundary condition meshes."""
+ """Bi-level optimizer for bounded meshes (`BoundedMesh`)."""
def __init__(self) -> None:
- """Create a Bi-level optimizer for periodic boundary condition meshes with default parameters."""
+ """Create a Bi-level optimizer for bounded meshes with default parameters.
+
+ Does not set the inner and outer loss functions yet.
+ """
super().__init__()
self._inner_opt_func = self._inner_opt
self._outer_opt_func = self._outer_opt
diff --git a/src/vertax/bilevelopt/pbcbop.py b/src/vertax/bilevelopt/pbcbop.py
index 5c3f75f..a1bbcdf 100644
--- a/src/vertax/bilevelopt/pbcbop.py
+++ b/src/vertax/bilevelopt/pbcbop.py
@@ -19,10 +19,13 @@
class PbcBilevelOptimizer(_BilevelOptimizer):
- """Bi-level optimizer for periodic boundary condition meshes."""
+ """Bi-level optimizer for periodic boundary condition meshes (`PbcMesh`)."""
def __init__(self) -> None:
- """Create a Bi-level optimizer for periodic boundary condition meshes with default parameters."""
+ """Create a Bi-level optimizer for periodic boundary condition meshes with default parameters.
+
+ Does not specify the inner and outer loss yet.
+ """
super().__init__()
self._inner_opt_func = self._inner_opt
self._outer_opt_func = self._outer_opt
@@ -159,7 +162,14 @@ def _outer_opt(
else:
match self.bilevel_optimization_method:
case BilevelOptimizationMethod.AUTOMATIC_DIFFERENTIATION:
- mesh.vertices_params, mesh.edges_params, mesh.faces_params = outer_opt(
+ (
+ mesh.vertices,
+ mesh.edges,
+ mesh.faces,
+ mesh.vertices_params,
+ mesh.edges_params,
+ mesh.faces_params,
+ ) = outer_opt(
mesh.vertices,
mesh.edges,
mesh.faces,
@@ -187,7 +197,14 @@ def _outer_opt(
)
case BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION:
- mesh.vertices_params, mesh.edges_params, mesh.faces_params = outer_eq_prop(
+ (
+ mesh.vertices,
+ mesh.edges,
+ mesh.faces,
+ mesh.vertices_params,
+ mesh.edges_params,
+ mesh.faces_params,
+ ) = outer_eq_prop(
mesh.vertices,
mesh.edges,
mesh.faces,
@@ -216,7 +233,14 @@ def _outer_opt(
)
case BilevelOptimizationMethod.IMPLICIT_DIFFERENTIATION:
- mesh.vertices_params, mesh.edges_params, mesh.faces_params = outer_implicit(
+ (
+ mesh.vertices,
+ mesh.edges,
+ mesh.faces,
+ mesh.vertices_params,
+ mesh.edges_params,
+ mesh.faces_params,
+ ) = outer_implicit(
mesh.vertices,
mesh.edges,
mesh.faces,
@@ -244,7 +268,14 @@ def _outer_opt(
)
case BilevelOptimizationMethod.ADJOINT_STATE:
- mesh.vertices_params, mesh.edges_params, mesh.faces_params = outer_adjoint_state(
+ (
+ mesh.vertices,
+ mesh.edges,
+ mesh.faces,
+ mesh.vertices_params,
+ mesh.edges_params,
+ mesh.faces_params,
+ ) = outer_adjoint_state(
mesh.vertices,
mesh.edges,
mesh.faces,
diff --git a/src/vertax/bounded.py b/src/vertax/bounded.py
deleted file mode 100644
index 1699f49..0000000
--- a/src/vertax/bounded.py
+++ /dev/null
@@ -1,1051 +0,0 @@
-"""Bounded mesh with arc circles for boundary cells."""
-
-from __future__ import annotations
-
-from collections.abc import Callable
-from pathlib import Path
-from typing import TYPE_CHECKING, Literal, Self
-
-import jax
-import jax.numpy as jnp
-import matplotlib
-import matplotlib.pyplot as plt
-import numpy as np
-from matplotlib.axes import Axes
-from matplotlib.figure import Figure
-from scipy.spatial import Voronoi
-
-from vertax.geo import get_any_length, get_area_bounded, get_perimeter_bounded
-from vertax.mesh import Mesh
-from vertax.method_enum import BilevelOptimizationMethod
-from vertax.opt_bounded import (
- InnerLossFunctionBounded,
- OuterLossFunction,
- UpdateT1FuncBounded,
- inner_opt_bounded,
- outer_eq_prop_bounded,
- outer_implicit_bounded,
- outer_opt_bounded,
-)
-from vertax.plot import EdgePlot, FacePlot, VertexPlot, add_colorbar, adjust_lightness, get_cmap
-from vertax.topo import do_not_update_T1_bounded, update_T1_bounded
-
-if TYPE_CHECKING:
- from jax import Array
- from matplotlib.pylab import Generator
- from numpy.typing import NDArray
-
-
-class BoundedMesh(Mesh):
- """Bounded mesh with arc circles for boundary cells."""
-
- def __init__(self) -> None:
- """Do not call the constructor."""
- super().__init__()
- self.angles: Array = jnp.array([])
- self.angles_target: Array = jnp.array([])
- self._update_T1_func: UpdateT1FuncBounded = update_T1_bounded
-
- def save_mesh_txt(
- self,
- directory: str,
- vertices_filename: str = "vertTable.txt",
- angles_filename: str = "angTable.txt",
- edges_filename: str = "heTable.txt",
- faces_filename: str = "faceTable.txt",
- ) -> None:
- """Save a mesh in separate text files that can be read by numpy.
-
- Only save the vertices, angles, edges and faces, not other parameters.
-
- Args:
- directory (str): Path to the directory where to save the files.
- vertices_filename (str, optional): Filename for the vertices table. Defaults to "vertTable.txt".
- angles_filename (str, optional): Filename for the angles table. Defaults to "angTable.txt".
- edges_filename (str, optional): Filename for the half-edges table. Defaults to "heTable.txt".
- faces_filename (str, optional): Filename for the faces table. Defaults to "faceTable.txt".
- """
- dirpath = Path(directory)
- dirpath.mkdir(parents=True, exist_ok=True)
- vertpath = dirpath / vertices_filename
- angpath = dirpath / angles_filename
- hepath = dirpath / edges_filename
- facepath = dirpath / faces_filename
- np.savetxt(vertpath, self.vertices)
- np.savetxt(angpath, self.angles)
- np.savetxt(hepath, self.edges)
- np.savetxt(facepath, self.faces)
-
- def save_mesh(self, path: str) -> None:
- """Save mesh to a file.
-
- All BoundedMesh data is saved, except for the solvers that are not saved.
-
- Args:
- path (str): Path to the saved file. The extension is .npz.
- """
- Path(path).parent.mkdir(parents=True, exist_ok=True)
- np.savez_compressed(
- path,
- allow_pickle=False,
- vertices=self.vertices,
- edges=self.edges,
- faces=self.faces,
- angles=self.angles,
- width=self.width,
- height=self.height,
- vertices_params=self.vertices_params,
- edges_params=self.edges_params,
- faces_params=self.faces_params,
- vertices_target=self.vertices_target,
- edges_target=self.edges_target,
- faces_target=self.faces_target,
- angles_target=self.angles_target,
- image_target=self.image_target,
- bilevel_optimization_method=self.bilevel_optimization_method.value,
- beta=self.beta,
- min_dist_T1=self.min_dist_T1,
- max_nb_iterations=self.max_nb_iterations,
- tolerance=self.tolerance,
- patience=self.patience,
- update_t1=self.update_t1,
- )
-
- @classmethod
- def load_mesh(cls, path: str) -> Self:
- """Load a mesh from a file.
-
- All BoundedMesh data is reloaded, except for the solvers that are not saved.
-
- Args:
- path (str): Path to the mesh file (.npz).
-
- Returns:
- Mesh: the mesh loaded from the .npz file.
- """
- mesh_file = np.load(path)
- mesh = cls._create()
- mesh.vertices, mesh.edges, mesh.faces, mesh.angles = (
- mesh_file["vertices"],
- mesh_file["edges"],
- mesh_file["faces"],
- mesh_file["angles"],
- )
- mesh.width = mesh_file["width"]
- mesh.height = mesh_file["height"]
- mesh.vertices_params = mesh_file["vertices_params"]
- mesh.edges_params = mesh_file["edges_params"]
- mesh.faces_params = mesh_file["faces_params"]
- mesh.vertices_target = mesh_file["vertices_target"]
- mesh.edges_target = mesh_file["edges_target"]
- mesh.faces_target = mesh_file["faces_target"]
- mesh.angles_target = mesh_file["angles_target"]
- mesh.image_target = mesh_file["image_target"]
- mesh.bilevel_optimization_method = BilevelOptimizationMethod(mesh_file["bilevel_optimization_method"])
- mesh.beta = mesh_file["beta"]
- mesh.min_dist_T1 = mesh_file["min_dist_T1"]
- mesh.max_nb_iterations = mesh_file["max_nb_iterations"]
- mesh.tolerance = mesh_file["tolerance"]
- mesh.patience = mesh_file["patience"]
- mesh.update_t1 = mesh_file["update_t1"]
- return mesh
-
- @classmethod
- def load_mesh_txt(
- cls,
- directory: str,
- vertices_filename: str = "vertTable.txt",
- angles_filename: str = "angTable.txt",
- edges_filename: str = "heTable.txt",
- faces_filename: str = "faceTable.txt",
- ) -> Self:
- """Load a mesh from text files.
-
- Only load the vertices, angles, edges and faces, not other parameters.
-
- Args:
- directory (str): Directory where the text files are stored.
- vertices_filename (str, optional): Filename for the vertices table. Defaults to "vertTable.txt".
- angles_filename (str, optional): Filename for the angles table. Defaults to "angTable.txt".
- edges_filename (str, optional): Filename for the half-edges table. Defaults to "heTable.txt".
- faces_filename (str, optional): Filename for the faces table. Defaults to "faceTable.txt".
-
- Returns:
- Self: The loaded mesh.
- """
- dirpath = Path(directory)
- dirpath.mkdir(parents=True, exist_ok=True)
- vertpath = dirpath / vertices_filename
- angpath = dirpath / angles_filename
- hepath = dirpath / edges_filename
- facepath = dirpath / faces_filename
-
- mesh = cls._create()
- mesh.vertices = jnp.array(np.loadtxt(vertpath, dtype=np.float64))
- mesh.angles = jnp.array(np.loadtxt(angpath, dtype=np.float64))
- mesh.edges = jnp.array(np.loadtxt(hepath, dtype=np.int64))
- mesh.faces = jnp.array(np.loadtxt(facepath, dtype=np.int64))
- return mesh
-
- @classmethod
- def copy_mesh(cls, other_mesh: Self) -> Self:
- """Copy all parameters from another mesh in a new mesh."""
- mesh = cls._create()
- mesh.vertices = other_mesh.vertices.copy()
- mesh.edges = other_mesh.edges.copy()
- mesh.faces = other_mesh.faces.copy()
- mesh.angles = other_mesh.angles.copy()
- mesh.angles_target = other_mesh.angles_target.copy()
- mesh.width = other_mesh.width
- mesh.height = other_mesh.height
- mesh.vertices_params = other_mesh.vertices_params.copy()
- mesh.edges_params = other_mesh.edges_params.copy()
- mesh.faces_params = other_mesh.faces_params.copy()
- mesh.vertices_target = other_mesh.vertices_target.copy()
- mesh.edges_target = other_mesh.edges_target.copy()
- mesh.faces_target = other_mesh.faces_target.copy()
- mesh.image_target = other_mesh.image_target.copy()
- mesh.bilevel_optimization_method = other_mesh.bilevel_optimization_method
- mesh.beta = other_mesh.beta
- mesh.min_dist_T1 = other_mesh.min_dist_T1
- mesh.max_nb_iterations = other_mesh.max_nb_iterations
- mesh.tolerance = other_mesh.tolerance
- mesh.patience = other_mesh.patience
- mesh.inner_solver = other_mesh.inner_solver
- mesh.outer_solver = other_mesh.outer_solver
- mesh._update_T1_func = other_mesh._update_T1_func
-
- return mesh
-
- @property
- def update_t1(self) -> bool:
- """Whether or not update the mesh by applying T1 transitions."""
- return self._update_T1_func != do_not_update_T1_bounded
-
- @update_t1.setter
- def update_t1(self, b: bool) -> None:
- """Whether or not update the mesh by applying T1 transitions."""
- if b:
- self._update_T1_func = update_T1_bounded
- else:
- self._update_T1_func = do_not_update_T1_bounded
-
- @property
- def nb_angles(self) -> int:
- """Get the number of angles of the mesh."""
- return len(self.angles)
-
- def get_length(self, half_edge_id: Array) -> Array:
- """Get the length of an edge."""
- vertTable = jnp.vstack([jnp.array([[0.0, 0.0], [1.0, 1.0]]), self.vertices])
- angTable = jnp.repeat(self.angles, 2)
-
- def _get_length(half_edge_id: Array) -> Array:
- return get_any_length(half_edge_id, vertTable, angTable, self.edges)
-
- return jax.vmap(_get_length)(half_edge_id)
-
- def get_perimeter(self, face_id: Array) -> Array:
- """Get the area of a face."""
- vertTable = jnp.vstack([jnp.array([[0.0, 0.0], [1.0, 1.0]]), self.vertices])
- angTable = jnp.repeat(self.angles, 2)
-
- def _get_perimeter(face_id: Array) -> Array:
- return get_perimeter_bounded(face_id, vertTable, angTable, self.edges, self.faces)
-
- return jax.vmap(_get_perimeter)(face_id)
-
- def get_area(self, face_id: Array) -> Array:
- """Get the area of a face."""
- vertTable = jnp.vstack([jnp.array([[0.0, 0.0], [1.0, 1.0]]), self.vertices])
- angTable = jnp.repeat(self.angles, 2)
-
- def _get_area(face_id: Array) -> Array:
- return get_area_bounded(face_id, vertTable, angTable, self.edges, self.faces)
-
- return jax.vmap(_get_area)(face_id)
-
- @classmethod
- def from_random_seeds(cls, nb_seeds: int, width: float, height: float, random_key: int, nb_fates: int = 2) -> Self:
- """Create a bounded Mesh from random seeds.
-
- Args:
- nb_seeds (int): Number of random seeds to use.
- width (float): width of the box containing the seeds.
- height (float): height of the box containing the seeds.
- random_key (int): seed for a random number generator to add new seeds if needed, decide on cell fates...
- nb_fates (int, default=2): number of possible different fate marker for a cell.
-
- Returns:
- Self: The corresponding mesh.
- """
- rng = np.random.default_rng(seed=random_key)
- seeds = rng.random((nb_seeds, 2)) * (width, height)
- return cls.from_seeds(seeds, width, height, random_key, nb_fates)
-
- @classmethod
- def from_seeds(cls, seeds: NDArray, width: float, height: float, random_key: int, nb_fates: int = 2) -> Self: # noqa: C901
- """Create a bounded Mesh from a list of seeds.
-
- The seeds are assumed to have x-coordinate in ]0, width[ and y-coordinate in ]0, height[.
- Note that the final mesh might not use your seeds if they don't work to create a correct
- bounded mesh via our method.
-
- Args:
- seeds (Array[float32]): jax float array of seed positions of shape (nbSeeds, 2).
- width (float): width of the box containing the seeds.
- height (float): height of the box containing the seeds.
- random_key (int): seed for a random number generator to add new seeds if needed, decide on cell fates...
- nb_fates (int, default=2): number of possible different fate marker for a cell.
- """
- rng = np.random.default_rng(seed=random_key)
- n_cells = len(seeds) # starting number of seeds must be equal to the desired number of cells (faces)
-
- # We'll try to construct a Voronoi diagram with n_cells closed (bounded) cells.
- # This will not work with exactly n_cells seeds because there will be unbounded cells.
- # If the number of bounded cells is insufficient, we add a new seed to the list.
- # If there is too many bounded cells, we retry with entirely new seeds.
- # We also check that the bounded cells are connected.
- while True:
- success = 0 # if 1 : not enough bounded cells. If 2 : too many bounded cells.
-
- # Create the Voronoi diagrams from current seed list.
- voronoi = Voronoi(seeds)
- vertices = voronoi.vertices
- edges = voronoi.ridge_vertices
- faces = voronoi.regions # regions = faces = cells
-
- # We count the number of bounded cells and the connectivity of vertices.
- inbound_faces = []
- inbound_vertices = np.zeros(vertices.shape[0], dtype=np.int32)
- for face in faces:
- if face and all(item > -1 for item in face): # the face must not be an empty list
- face_vertices_positions = vertices[face]
- # We check that all of the face's vertices are in a box [0, width]x[0, height]
- if (
- np.all(face_vertices_positions[:, 0] < width)
- and np.all(face_vertices_positions[:, 1] < height)
- and np.all(face_vertices_positions > 0)
- ):
- inbound_faces.append(face) # the face is bounded
- inbound_vertices[face] += 1 # +1 to the connectivity of the face's vertices.
-
- # getting rid of faces connected to a single other inbound face
- # (these can be problematic and lead to many special cases later on)
- while True:
- num_infaces = len(inbound_faces)
- del_count = 0
- for i, face in enumerate(reversed(inbound_faces)):
- # only 2 (or less (not sure it's possible)) vertices of the face are shared with other faces :
- # that means it is connected to one other bounded face only -> We remove it.
- if np.sum(inbound_vertices[face] > 1) <= 2:
- inbound_vertices[face] -= 1
- del inbound_faces[num_infaces - i - 1]
- del_count += 1
- # Removing one face can possibly alter other faces so we might do another loop.
- # We stop when there is no more face to remove.
- if del_count == 0:
- break
-
- # Check that we have the correct number of cells.
- if num_infaces < n_cells:
- success = 1
- elif num_infaces > n_cells:
- success = 2
- else:
- # There is exactly n_cells connected bounded faces.
- # Now, it is possible that a bounded face has vertices or edges
- # that are not shared with other faces. We get rid of those,
- # in order to have only one exterior edge that will be an arc circle.
- for i, face in enumerate(inbound_faces):
- useful_vertices = [] # List of the face vertices that are shared with other.
- # (We'll keep them and call them "useful").
- extra_edges = [] # List of edges to replace what we have removed
- last_useful = -1 # ID of the last "useful" vertex. -1 at the beginning (we'll treat that case)
- new_edge = [] # New edge that will replace current vertices we're trying to remove
- incomplete_new_edge = False # State boolean : are we replacing vertices right now ?
- for vertex in face:
- if inbound_vertices[vertex] == 1: # We found a vertex that is not shared with other faces.
- # We plan to remove it by not adding it to the useful vertices list,
- # and by creating a new edge from last useful vertex to the next one.
- if not incomplete_new_edge: # Detect if we're not already in the incomplete edge state
- new_edge = [] # re-init
- new_edge.append(last_useful)
- incomplete_new_edge = True # Move to incomplete edge state.
- else: # We found a useful vertex
- useful_vertices.append(vertex)
- last_useful = vertex
- if incomplete_new_edge: # If in incomplete edge state we can finally close the new edge.
- new_edge.append(vertex)
- extra_edges.append(new_edge)
- incomplete_new_edge = False
- # After looping through the vertices of the face, we need to take care of
- # two special cases : the first or the last vertex is not shared.
- if extra_edges and extra_edges[0][0] == -1:
- extra_edges[0][0] = useful_vertices[-1]
- elif incomplete_new_edge:
- new_edge.append(useful_vertices[0])
- extra_edges.append(new_edge)
- # The extra edges are added to the list of all edges
- edges.extend(extra_edges)
- inbound_faces[i] = tuple(
- sorted(useful_vertices)
- ) # And the face itself is replaced by only the useful vertices.
- # Note that the vertices here are not ordered in clockwise or counterclockwise order anymore.
- useful_vertices_set = set(np.where(inbound_vertices > 1)[0]) # We filter the useful vertices.
-
- # HALF EDGE DATA STRUCTURE
- # Filter edges with useful vertices only.
- useful_edges = [tuple(sorted(e)) for e in edges if set(e).issubset(useful_vertices_set)]
-
- # failing to abide by the following relation results in disconnected topologies
- if len(useful_edges) != (n_cells - 1) * 3:
- success = 2 # Case : we want to restart with new seeds because current solution is not OK.
- else:
- # We construct the half-edges.
- half_edges = []
- for e in useful_edges:
- half_edges.append(e)
- # reciprocating edges
- half_edges.append((e[1], e[0]))
-
- # finding clockwise (or counterclockwise) half edge set for each face,
- # as we broke it earlier.
- ordered_edges_inbound_faces = []
- for face in inbound_faces:
- # Find all edges fon this face and we'll loow through them to order them.
- edges_face = [(f1, f2) for f1 in face for f2 in face if (f1, f2) in useful_edges]
-
- i = 0
- start_edge = edges_face[i]
- ordered_face = [start_edge]
- e = start_edge
- visited = [e]
- while sorted(edges_face) != sorted(visited):
- if e[0] == start_edge[1] and e not in visited:
- ordered_face.append(e)
- start_edge = e
- visited.append(e)
- # We must be careful because some edges might be in the wrong order.
- if e[1] == start_edge[1] and e not in visited:
- ordered_face.append((e[1], e[0]))
- start_edge = (e[1], e[0])
- visited.append(e)
- i += 1
- e = edges_face[i % len(edges_face)]
-
- # Sanity check : do we have a correct ordering ?
- order = 0
- for e in ordered_face:
- idx0 = e[0]
- idx1 = e[1]
-
- order += (vertices[idx1][0] - vertices[idx0][0]) * (vertices[idx1][1] + vertices[idx0][1])
-
- if order < 0:
- ordered_edges_inbound_faces.append(ordered_face)
- if order > 0:
- ordered_edges_inbound_faces.append([(e[1], e[0]) for e in reversed(ordered_face)])
- if order == 0:
- print("\nError: no order detected for face " + str(face) + "\n")
- exit()
-
- # Now we fill the tables with the info we have.
- useful_vertices_list = list(useful_vertices_set)
- vertTable = np.zeros((len(useful_vertices_list), 2))
- for i, idx in enumerate(useful_vertices_list):
- pos = vertices[idx]
- vertTable[i][0] = pos[0] # x pos vert
- vertTable[i][1] = pos[1] # y pos vert
-
- faceTable = np.zeros((len(inbound_faces), 1), dtype=np.int32)
- for i, hedges_face in enumerate(ordered_edges_inbound_faces):
- for j, he in enumerate(half_edges):
- if he == hedges_face[0]:
- faceTable[i] = j # he_inside
- faceTable = _fate_selection(faceTable, nb_fates, rng)
-
- nb_half_edges = len(half_edges)
- heTable = np.zeros((nb_half_edges, 8), dtype=np.int32)
- heTable[:, 4] = 1
- heTable[:, 6] = 1
- relevant_twins = []
- # HE TABLE :
- # 0 : previous half-edge.
- # 1 : next half-edge.
- # 2 : twin half-edge.
- # 3 : source vertex id + 2 if edge is inside, 0 if the edge is outside
- # 4 : target vertex id + 2 if edge is inside, 0 if the edge is outside
- # 5 : source vertex id + 2 if edge is outside, 0 if the edge is inside
- # 6 : target vertex id + 2 if edge is outside, 0 if the edge is inside
- # 7 : id of the face containing this half-edge.
- for i, he in enumerate(half_edges):
- belongs_to_any_face = False
- for hedges_face in ordered_edges_inbound_faces:
- if he in hedges_face:
- idx = hedges_face.index(he)
- heTable[i][0] = half_edges.index(hedges_face[(idx - 1) % len(hedges_face)]) # he_prev
- heTable[i][1] = half_edges.index(hedges_face[(idx + 1) % len(hedges_face)]) # he_next
- # indices 0 and 1 are reserved for source or target vertices of "outside" edges.
- # So we have to add +2 to other indices.
- heTable[i][3] = useful_vertices_list.index(he[0]) + 2 # vert source inner edges
- heTable[i][4] = useful_vertices_list.index(he[1]) + 2 # vert target inner edges
- heTable[i][7] = ordered_edges_inbound_faces.index(hedges_face) # face
- belongs_to_any_face = True
- break
- twin_idx = half_edges.index((he[1], he[0]))
- heTable[i][2] = twin_idx # he twin
- if not belongs_to_any_face:
- relevant_twins.append(twin_idx)
-
- # Angles are randomly chosen between 0 and pi/2 (with some margin to avoid extreme cases).
- angTable = np.ones(nb_half_edges // 2)
- for tidx in relevant_twins:
- angTable[tidx // 2] = rng.random() * (np.pi / 2 - 0.018) + 0.017
- heTable[tidx][5] = heTable[tidx][3] # vert source surface edges
- heTable[tidx][6] = heTable[tidx][4] # vert target surface edges
- heTable[tidx][3] = 0
- heTable[tidx][4] = 1
-
- bounded_mesh = cls._create()
- bounded_mesh.vertices = jnp.array(vertTable, dtype=np.float32)
- bounded_mesh.angles = jnp.array(angTable, dtype=np.float32)
- bounded_mesh.faces = jnp.array(faceTable, dtype=np.int32)
- bounded_mesh.edges = jnp.array(heTable, dtype=np.int32)
- bounded_mesh.width = width
- bounded_mesh.height = height
-
- return bounded_mesh
-
- # If success was 1, we had not enough bounded faces, we add a new seed to see if it helps.
- # Otherwise we retry with new seeds entirely.
- seeds = (
- np.vstack([seeds, (width, height) * rng.random((1, 2))])
- if success == 1
- else (width, height) * rng.random((n_cells, 2))
- ) # type: ignore
-
- def inner_opt(
- self,
- loss_function_inner: InnerLossFunctionBounded,
- only_on_vertices: None | list[int] = None,
- only_on_edges: None | list[int] = None,
- only_on_faces: None | list[int] = None,
- ) -> list[float]:
- """Optimize the mesh for the loss function given.
-
- Args:
- loss_function_inner (InnerLossFunction): Loss function to optimize.
- only_on_vertices (None | list[int] = None): Consider only a subset of vertices for the loss function.
- All vertices if None.
- only_on_edges (None | list[int] = None): Consider only a subset of edges for the loss function.
- All edges if None.
- only_on_faces (None | list[int] = None): Consider only a subset of faces for the loss function.
- All faces if None.
-
- Returns:
- list[float]: History of loss values during optimization.
- """
- selected_vertices, selected_edges, selected_faces = None, None, None
- if only_on_vertices is not None:
- selected_vertices = jnp.array(only_on_vertices)
- if only_on_edges is not None:
- selected_edges = jnp.array(only_on_edges)
- if only_on_faces is not None:
- selected_faces = jnp.array(only_on_faces)
-
- (self.vertices, self.angles, self.edges, self.faces), energies = inner_opt_bounded(
- self.vertices,
- self.angles,
- self.edges,
- self.faces,
- self.vertices_params,
- self.edges_params,
- self.faces_params,
- loss_function_inner,
- self.inner_solver,
- self.min_dist_T1,
- self.max_nb_iterations,
- self.tolerance,
- self.patience,
- selected_verts=selected_vertices,
- selected_hes=selected_edges,
- selected_faces=selected_faces,
- update_T1_func=self._update_T1_func,
- )
- return list(energies)
-
- def outer_opt(
- self,
- loss_function_inner: InnerLossFunctionBounded,
- loss_function_outer: OuterLossFunction,
- only_on_vertices: None | list[int] = None,
- only_on_edges: None | list[int] = None,
- only_on_faces: None | list[int] = None,
- ) -> None:
- """Outer optimization depending on the optimization method."""
- selected_vertices, selected_edges, selected_faces = None, None, None
- if only_on_vertices is not None:
- selected_vertices = jnp.array(only_on_vertices)
- if only_on_edges is not None:
- selected_edges = jnp.array(only_on_edges)
- if only_on_faces is not None:
- selected_faces = jnp.array(only_on_faces)
- match self.bilevel_optimization_method:
- case BilevelOptimizationMethod.AUTOMATIC_DIFFERENTIATION:
- self.vertices_params, self.edges_params, self.faces_params = outer_opt_bounded(
- self.vertices,
- self.angles,
- self.edges,
- self.faces,
- self.vertices_params,
- self.edges_params,
- self.faces_params,
- self.vertices_target,
- self.angles_target,
- self.edges_target,
- self.faces_target,
- loss_function_inner,
- loss_function_outer,
- self.inner_solver,
- self.outer_solver,
- self.min_dist_T1,
- self.max_nb_iterations,
- self.tolerance,
- self.patience,
- selected_vertices,
- selected_edges,
- selected_faces,
- self.image_target,
- self._update_T1_func,
- )
- case BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION:
- self.vertices_params, self.edges_params, self.faces_params = outer_eq_prop_bounded(
- self.vertices,
- self.angles,
- self.edges,
- self.faces,
- self.vertices_params,
- self.edges_params,
- self.faces_params,
- self.vertices_target,
- self.angles_target,
- self.edges_target,
- self.faces_target,
- loss_function_inner,
- loss_function_outer,
- self.inner_solver,
- self.outer_solver,
- self.min_dist_T1,
- self.max_nb_iterations,
- self.tolerance,
- self.patience,
- selected_vertices,
- selected_edges,
- selected_faces,
- self.image_target,
- self.beta,
- self._update_T1_func,
- )
- case BilevelOptimizationMethod.IMPLICIT_DIFFERENTIATION:
- self.vertices_params, self.edges_params, self.faces_params = outer_implicit_bounded(
- self.vertices,
- self.angles,
- self.edges,
- self.faces,
- self.vertices_params,
- self.edges_params,
- self.faces_params,
- self.vertices_target,
- self.angles_target,
- self.edges_target,
- self.faces_target,
- loss_function_inner,
- loss_function_outer,
- self.inner_solver,
- self.outer_solver,
- self.min_dist_T1,
- self.max_nb_iterations,
- self.tolerance,
- self.patience,
- selected_vertices,
- selected_edges,
- selected_faces,
- self.image_target,
- self._update_T1_func,
- )
- case _:
- msg = f"{self.bilevel_optimization_method} is not implemented for bounded meshes."
- raise ValueError(msg)
-
- def bilevel_opt(
- self,
- loss_function_inner: InnerLossFunctionBounded,
- loss_function_outer: OuterLossFunction,
- only_on_vertices: None | list[int] = None,
- only_on_edges: None | list[int] = None,
- only_on_faces: None | list[int] = None,
- ) -> list[float]:
- """Optimize the mesh for the loss function given.
-
- Args:
- loss_function_inner (InnerLossFunction): Loss function to optimize.
- loss_function_outer (OuterLossFunction): Loss function to optimize.
- only_on_vertices (None | list[int] = None): Consider only a subset of vertices for the loss function.
- All vertices if None.
- only_on_edges (None | list[int] = None): Consider only a subset of edges for the loss function.
- All edges if None.
- only_on_faces (None | list[int] = None): Consider only a subset of faces for the loss function.
- All faces if None.
-
- Returns:
- list[float]: History of loss values during optimization.
- """
- self.outer_opt(loss_function_inner, loss_function_outer, only_on_vertices, only_on_edges, only_on_faces)
- return self.inner_opt(loss_function_inner, only_on_vertices, only_on_edges, only_on_faces)
-
- def plot(
- self,
- vertex_plot: VertexPlot = VertexPlot.BLACK,
- edge_plot: EdgePlot = EdgePlot.BLACK,
- face_plot: FacePlot = FacePlot.MULTICOLOR,
- vertex_parameters_name: str = "",
- edge_parameters_name: str = "",
- face_parameters_name: str = "",
- show: bool = True,
- save: bool = False,
- save_path: str = "bounded_mesh.png",
- faces_cmap_name: str = "cividis",
- edges_cmap_name: str = "coolwarm",
- edges_width: float = 2,
- vertices_cmap_name: str = "spring",
- vertices_size: float = 20,
- title: str = "",
- ) -> None:
- """Plot the mesh and decide to save and/or show the mesh or not."""
- fig, _ax = self.get_plot(
- vertex_plot,
- edge_plot,
- face_plot,
- vertex_parameters_name,
- edge_parameters_name,
- face_parameters_name,
- faces_cmap_name,
- edges_cmap_name,
- edges_width,
- vertices_cmap_name,
- vertices_size,
- title,
- )
-
- if save:
- plt.savefig(save_path) # format maybe should be left as a choice
-
- if show:
- plt.show()
-
- plt.close(fig)
-
- def get_plot(
- self,
- vertex_plot: VertexPlot = VertexPlot.BLACK,
- edge_plot: EdgePlot = EdgePlot.BLACK,
- face_plot: FacePlot = FacePlot.MULTICOLOR,
- vertex_parameters_name: str = "",
- edge_parameters_name: str = "",
- face_parameters_name: str = "",
- faces_cmap_name: str = "cividis",
- edges_cmap_name: str = "coolwarm",
- edges_width: float = 2,
- vertices_cmap_name: str = "spring",
- vertices_size: float = 20,
- title: str = "",
- ) -> tuple[Figure, Axes]:
- """Get the matplotlib figure and and ax for one plot."""
- fig, _ = plt.subplots(layout="constrained")
- ax = plt.gca()
- self._plot_faces(fig, ax, face_plot, faces_cmap_name, face_parameters_name)
- self._plot_edges(fig, ax, edge_plot, edges_cmap_name, edges_width, edge_parameters_name)
- self._plot_vertices(fig, ax, vertex_plot, vertices_cmap_name, vertices_size, vertex_parameters_name)
-
- ax.set_title(title)
- # unlike the pbc case, here is not easy to know a priori
- # what the limits of the progressively optimized cell cluster will be (across a stack of images)
- ax.set_xlim(-0.5, self.width + 0.5)
- ax.set_ylim(-0.5, self.height + 0.5)
- ax.set_aspect((self.height + 1) / (self.width + 1))
-
- return fig, ax
-
- def _plot_faces(
- self, fig: Figure, ax: Axes, face_plot: FacePlot, faces_cmap_name: str, face_parameters_name: str
- ) -> None:
- multicolor_cmap = self._get_multicolor_face_cmap()
- faces_cmap = matplotlib.colormaps.get_cmap(faces_cmap_name)
-
- v_max = 1
- v_min = 0
- values = jnp.array([1])
- # set the correct colorbar if needed
- # Get values, min and max
- cbar_label = "Face parameter" if face_parameters_name == "" else face_parameters_name
- match face_plot:
- case FacePlot.FACE_PARAMETER:
- values = self.faces_params
- case FacePlot.AREA:
- values = self.get_area(jnp.arange(self.nb_faces))
- cbar_label = "Area of cell"
- case FacePlot.PERIMETER:
- values = self.get_perimeter(jnp.arange(self.nb_faces))
- cbar_label = "Perimeter of cell"
- case FacePlot.FATES:
- values = self.faces[:, 1]
- v_max = float(values.max())
- v_min = float(values.min())
- match face_plot:
- case FacePlot.MULTICOLOR | FacePlot.WHITE | FacePlot.FATES:
- pass
- case _:
- cbar = add_colorbar(fig, ax, v_min, v_max, faces_cmap)
- cbar.ax.set_ylabel(cbar_label, rotation=270, labelpad=13)
- cbar.ax.yaxis.set_ticks_position("left")
-
- # Draw each face
- for face in range(len(self.faces)):
- match face_plot:
- # Find correct color depending on chosen colormap
- case FacePlot.MULTICOLOR:
- color = multicolor_cmap(face)
- case FacePlot.WHITE:
- color = (1, 1, 1, 1)
- case _:
- norm_val = 1 if v_max == v_min else (values[face] - v_min) / (v_max - v_min)
- color = faces_cmap(norm_val)
- # Find face's vertices and draw the corresponding polygon.
- face_vertices = self._get_face_vertices(face)
- ax.fill(face_vertices[:, 0], face_vertices[:, 1], color=color)
-
- def _get_face_vertices(self, face_id: int) -> NDArray:
- """Get all vertices positions corresponding to a face."""
- face_vertices = []
-
- draw_curve_threshold = 0.01 # radians. Must be above 0 to avoid overcomplicating a simple plot
- # one will be 0, other real id offsetted by 2 as the first two values 0 and 1 are reserved
- # initialize the loop on every face's edges.
- start_he = int(self.faces[face_id][0])
- he = start_he
-
- # Do the while loop at least one time and add a max iter to "catch" bugs of infinite loop (should not happen !)
- do_while = False
- max_iter = 1000
- nb_iter = 0
- while not do_while or ((do_while and he != start_he) and nb_iter < max_iter):
- do_while = True # Just for the first time
- nb_iter += 1
- # Always add origin point
- v_source_id = int(self.edges[he][3] + self.edges[he][5] - 2)
- pos_source = self.vertices[v_source_id]
-
- face_vertices.append(pos_source)
- ang = float(self.angles[he // 2])
- if self.edges[he][3] != 0 or ang <= draw_curve_threshold:
- # means it's an inside edge, we do not need to do anything,
- # as the target will be the source of next half-edge. Or the angle is too flat.
- pass
- else: # means it's an outside edge so it might be drawn as an arc
- # one will be 1, other real id offsetted by 2 as the first two values 0 and 1 are reserved
- # so real_id + 2 - 1
- v_target_id = int(self.edges[he][4] + self.edges[he][6] - 3)
- pos_target = self.vertices[v_target_id]
- arc_points = _get_arc_with_n_points(ang, pos_source, pos_target)
- face_vertices.extend(arc_points)
-
- # Next edge for next loop
- he = int(self.edges[he][1])
-
- face_vertices.append(face_vertices[0])
- return np.array(face_vertices)
-
- def _get_multicolor_face_cmap(self) -> Callable[[int], tuple[float, float, float, float]]:
- def cmap_light_hsv(n: int) -> Callable[[int], tuple[float, float, float, Literal[1]]]:
- def light_hsv(i: int) -> tuple[float, float, float, Literal[1]]:
- fun: Callable[[int], tuple[float, float, float, float]] = get_cmap(n, name="hsv")
- return (*adjust_lightness(fun(i)[:3], 1.4), 1)
-
- return light_hsv
-
- return cmap_light_hsv(len(self.faces))
-
- def _plot_edges(
- self,
- fig: Figure,
- ax: Axes,
- edge_plot: EdgePlot,
- edges_cmap_name: str,
- edges_width: float,
- edge_parameters_name: str,
- ) -> None:
- if edge_plot != EdgePlot.INVISIBLE:
- edge_params_cmap = matplotlib.colormaps.get_cmap(edges_cmap_name)
- # set the correct colorbar
-
- v_max = 1
- v_min = 0
- values = jnp.array([1])
- # Get values, min and max
- cbar_label = "Edge parameter" if edge_parameters_name == "" else edge_parameters_name
- match edge_plot:
- case EdgePlot.EDGE_PARAMETER:
- values = self.edges_params
- case EdgePlot.LENGTH:
- values = self.get_length(jnp.arange(2 * self.nb_edges))
- cbar_label = "Length of edge"
- v_max = float(values.max())
- v_min = float(values.min())
-
- # set the correct colorbar if needed
- match edge_plot:
- case EdgePlot.BLACK:
- pass
- case _:
- cbar = add_colorbar(fig, ax, v_min, v_max, edge_params_cmap)
- cbar.ax.set_ylabel(cbar_label, rotation=270, labelpad=13)
- cbar.ax.yaxis.set_ticks_position("left")
-
- # Draw each edge
- for i in range(len(self.edges)):
- he = i
- # Find correct color depending on chosen colormap
- match edge_plot:
- case EdgePlot.BLACK:
- color = (0, 0, 0, 1)
- case _:
- norm_val = 1 if v_max == v_min else (values[he] - v_min) / (v_max - v_min)
- color = edge_params_cmap(norm_val)
- # Draw the edge with color
- self._draw_edge(ax, he, color, edges_width)
-
- def _draw_edge(
- self,
- ax: Axes,
- he: int,
- color: tuple[float, float, float, float] | NDArray,
- edges_width: float,
- ) -> None:
- draw_curve_threshold = 0.01 # radians. Must be above 0 to avoid overcomplicating a simple plot
-
- v_source_id = int(self.edges[he][3] + self.edges[he][5] - 2)
- v_target_id = int(self.edges[he][4] + self.edges[he][6] - 3)
- if v_source_id >= 0 and v_target_id >= 0: # else = twin of surface edges
- pos_source = self.vertices[v_source_id]
- pos_target = self.vertices[v_target_id]
- ang = float(self.angles[he // 2])
-
- if self.edges[he][3] != 0 or ang <= draw_curve_threshold:
- points = np.array([pos_source, pos_target])
- else:
- points = np.vstack(
- (pos_source, np.array(_get_arc_with_n_points(ang, pos_source, pos_target)), pos_target)
- )
- x = points[:, 0]
- y = points[:, 1]
- ax.plot(x, y, color=color, linewidth=edges_width)
-
- def _plot_vertices(
- self,
- fig: Figure,
- ax: Axes,
- vertex_plot: VertexPlot,
- vertices_cmap_name: str,
- vertices_size: float,
- vertex_parameters_name: str,
- ) -> None:
- if vertex_plot != VertexPlot.INVISIBLE:
- # set the correct colorbar
- vertices_params_cmap = matplotlib.colormaps.get_cmap(vertices_cmap_name)
- v_max = 1
- v_min = 0
- if vertex_plot == VertexPlot.VERTEX_PARAMETER:
- v_max = float(self.vertices_params.max())
- v_min = float(self.vertices_params.min())
- cbar = add_colorbar(fig, ax, v_min, v_max, vertices_params_cmap)
- cbar_label = "Vertex parameter" if vertex_parameters_name == "" else vertex_parameters_name
- cbar.ax.set_ylabel(cbar_label, rotation=270, labelpad=13)
- cbar.ax.yaxis.set_ticks_position("left")
- # Draw each vertex
- for i, vertex in enumerate(self.vertices):
- # Find correct color depending on chosen colormap
- match vertex_plot:
- case VertexPlot.VERTEX_PARAMETER:
- norm_val = 1 if v_max == v_min else (self.vertices_params[i] - v_min) / (v_max - v_min)
- color = vertices_params_cmap(norm_val)
- case VertexPlot.BLACK:
- color = (0, 0, 0, 1)
- case _:
- color = (0, 0, 0, 0)
- # Draw the vertex with color
- ax.scatter(vertex[0], vertex[1], color=color, s=vertices_size)
-
-
-def _fate_selection(faceTable: NDArray, n_fates: int, rng: Generator) -> NDArray:
- n_cells = faceTable.size
- n_cells_per_fate = n_cells // n_fates
- n_cells_left = n_cells % n_fates
- cell_fates = np.repeat(np.arange(n_fates), n_cells_per_fate)
- cell_fates = np.concatenate([cell_fates, np.arange(n_cells_left)])
- rng.shuffle(cell_fates)
- return np.hstack([faceTable, cell_fates[:, None]])
-
-
-def _get_arc_with_n_points(ang: float, pos_source: Array, pos_target: Array, nb_draw_points: int = 40) -> list[NDArray]:
- edge_vector = pos_target - pos_source
- edge_half_length = np.linalg.norm(edge_vector) / 2
- # shouldn't it be ang/2 ? No, ang is between 0 and pi/2, it's already divided by 2 (if you ask me)
- radius = edge_half_length / np.sin(ang)
- # diameter = 2 * radius
- d_midpoint_to_center = np.sqrt(radius**2 - edge_half_length**2)
- midpoint = (pos_source + pos_target) / 2
- unit_vector = edge_vector / np.linalg.norm(edge_vector)
- unit_vector_midpoint_to_center = np.array([-unit_vector[1], unit_vector[0]])
- center = midpoint + unit_vector_midpoint_to_center * d_midpoint_to_center
- # angles in radians between -pi and pi
- ang_source = np.angle(np.dot((pos_source - center), np.array([1, 1j]))) # angle of a + bi
- ang_target = np.angle(np.dot((pos_target - center), np.array([1, 1j])))
- # rad2deg = 180 / np.pi
- # # angle in degrees between -180 and 180
- # normalized_ang_source_degrees = ang_source * rad2deg
- # normalized_ang_target_degrees = ang_target * rad2deg
- # # angle in degrees between 0 and 360
- # if normalized_ang_source_degrees < 0.0:
- # normalized_ang_source_degrees += 360
- # if normalized_ang_target_degrees < 0.0:
- # normalized_ang_target_degrees += 360
-
- # # draw the curved edge
- # if lines:
- # surface_arc = Arc(
- # center,
- # diameter,
- # diameter,
- # theta1=normalized_ang_source_degrees,
- # theta2=normalized_ang_target_degrees,
- # color="black",
- # linewidth=2.0,
- # )
- # plt.gca().add_patch(surface_arc)
-
- # Now we'll take nb_draw_points the points along the curved edge and close the figure.
- tau = 2 * np.pi
- if abs(ang_target - ang_source) > np.pi: # can it happen ?
- if ang_source < ang_target:
- ang_source += tau
- else:
- ang_target += tau
- intermediate_angles = np.linspace(ang_source, ang_target, nb_draw_points, False)[1:] # without both endpoints
- # points = [pos_source]
- # points.extend([radius * np.array([np.cos(a), np.sin(a)]) + center for a in intermediate_angles])
- # points.append(pos_target)
- # points.append(pos_source) # make it a closed form
- # points = np.array(points)
- # x, y = points[:, 0], points[:, 1]
- return [np.array(radius * np.array([np.cos(a), np.sin(a)]) + center) for a in intermediate_angles]
diff --git a/src/vertax/cost.py b/src/vertax/cost.py
index 693a3c4..a341427 100644
--- a/src/vertax/cost.py
+++ b/src/vertax/cost.py
@@ -1,13 +1,57 @@
-"""Cost functions collection."""
+"""Cost functions collection, used for outer optimization.
+
+A cost function can be any user-defined function but it has to respect a strict signature.
+
+For a `PbcMesh` and `PbcBilevelOptimizer`, the cost function must have the following signature:
+ vertTable: Array,
+ heTable: Array,
+ faceTable: Array,
+ width: float,
+ height: float,
+ vertTable_target: Array,
+ heTable_target: Array,
+ faceTable_target: Array,
+ selected_verts: Array | None,
+ selected_hes: Array | None,
+ selected_faces: Array | None,
+ image_target: Array | None,
+and return an Array (or float).
+
+The names can vary and you can give default parameters. But the number and type of parameters is important.
+You don't have to use every parameters but they all have to be here.
+An unused parameters can of course also have the type None.
+
+Same for `BoundedMesh` and `BoundedBilevelOptimizer`, but with a slightly different signature:
+ vertTable: Array,
+ angTable: Array,
+ heTable: Array,
+ faceTable: Array,
+ vertTable_target: Array,
+ angTable_target: Array,
+ heTable_target: Array,
+ faceTable_target: Array,
+ selected_verts: Array | None,
+ selected_hes: Array | None,
+ selected_faces: Array | None,
+ image_target: Array | None,
+and return an Array (or float).
+
+Hopefully the variable names are self-explanatory.
+
+You can create a function with this signature exactly that uses also locally-accessible external variable if you want.
+"""
from functools import partial
import jax.numpy as jnp
+import numpy as np
from jax import Array, jit, vmap
from jax.lax import fori_loop
from jax.numpy import arange, array, diff, einsum, exp, expand_dims, int32, meshgrid, pi, sqrt, stack
from jax.numpy import sinc as npsinc
from jax.numpy.fft import ifft2
+from numpy.typing import NDArray
+from scipy.optimize import linear_sum_assignment
from vertax.geo import get_area
@@ -184,7 +228,7 @@ def cost_v2v(
selected_faces: Array | None = None,
_image_target: Array | None = None,
) -> Array:
- """Example of a cost function."""
+ """Cost vertex to vertex. Compare the positions of given vertices to target vertices (`PbcMesh`)."""
if selected_verts is None:
selected_verts = jnp.arange(vertTable.shape[0])
if selected_hes is None:
@@ -270,8 +314,9 @@ def cost_mesh2image(
_selected_faces: Array,
image_target: Array,
) -> Array:
- """Example of a cost function."""
- starting = (vertTable[heTable[selected_hes, 3], :2]) * 2 / [width, height] # (M, 2)
+ """Cost mesh to image. Compare the given vertices positions to a target image (`PbcMesh`)."""
+ wh = jnp.asarray([width, height])
+ starting = (vertTable[heTable[selected_hes, 3], :2]) * 2 / wh # (M, 2)
# ending = (vertTable[heTable[selected_hes, 4], :2]) * 2 / L_box # (M, 2)
ending = (
(
@@ -283,10 +328,10 @@ def cost_mesh2image(
],
axis=-1,
)
- * [width, height]
+ * wh
)
* 2
- / [width, height]
+ / wh
)
he_edges = stack((starting, ending), axis=1) # (N, 2, 2)
@@ -319,7 +364,7 @@ def cost_areas(
faceTable_target: Array,
_image_target: Array,
) -> Array:
- """Example of a cost function."""
+ """Cost areas : compare the areas of the given mesh versus the areas of the target mesh (`PbcMesh`)."""
def mapped_fn(f: Array) -> Array:
return (
@@ -346,7 +391,7 @@ def cost_ratio(
_selected_faces: Array | None = None,
_image_target: Array | None = None,
) -> Array:
- """Example of a cost function."""
+ """Cost that nudges a `BoundedMesh` to elongate along one axis while narrowing along the orthogonal axis."""
# Compute pairwise squared distances by broadcasting
# diff shape: (n, n, d)
diff = vertTable[:, None, :] - vertTable[None, :, :]
@@ -393,7 +438,10 @@ def cost_checkerboard(
_selected_faces: Array | None = None,
_image_target: Array | None = None,
) -> Array:
- """Example of a cost function."""
+ """Cost that nudges a `BoundedMesh` with different fated cells to avoid having neighboring cells with same fate.
+
+ This leads to a checkerboard pattern when there is 2 fates.
+ """
def body_fun(i: int, current_len: Array) -> Array:
idx = 2 * i
@@ -412,3 +460,431 @@ def body_fun(i: int, current_len: Array) -> Array:
n_edges = heTable.shape[0] // 2
return fori_loop(0, n_edges, body_fun, 0.0)
+
+
+@partial(jit, static_argnums=(3, 4))
+def cost_IAS( # noqa: C901, N802
+ vertTable: Array,
+ heTable: Array,
+ faceTable: Array,
+ _width: float,
+ _height: float,
+ vertTable_target: Array,
+ heTable_target: Array,
+ faceTable_target: Array,
+ _selected_verts: Array | None = None,
+ _selected_hes: Array | None = None,
+ _selected_faces: Array | None = None,
+ _image_target: Array | None = None,
+) -> Array:
+ r"""Differentiable Index Aware Structural Loss. Force to respect the topology.
+
+ C_{IAS}(i,j) = \sqrt{\sum_{k=1}^{N} (S_1(i,k) - S_2(j,k))^2}
+ """
+ L_box = jnp.sqrt(len(faceTable))
+
+ def l2(x: Array, y: Array) -> Array:
+ diff = x[:, None, :] - y[None, :, :]
+ return jnp.sqrt(jnp.sum(diff**2, axis=-1) + 1e-12)
+
+ def mse(x: Array, y: Array) -> Array:
+ diff = x[:, None, :] - y[None, :, :]
+ return jnp.sum(diff**2, axis=-1) + 1e-12
+
+ def sinkhorn(a: Array, b: Array, C: Array, eps: float = 5e-2, n_iters: int = 50) -> Array:
+ K = jnp.exp(-C / eps)
+ u = jnp.ones_like(a)
+ v = jnp.ones_like(b)
+
+ def body(_: int, state: tuple[Array, Array]) -> tuple[Array, Array]:
+ u, v = state
+ u = a / (K @ v + 1e-12)
+ v = b / (K.T @ u + 1e-12)
+ return (u, v)
+
+ u, v = fori_loop(0, n_iters, body, (u, v))
+ return jnp.outer(u, v) * K
+
+ # dual graph from half-edges
+ def build_dual_adj(heTable: Array, n_faces: int) -> Array:
+ face = heTable[:, 5]
+ twin = heTable[:, 2]
+
+ face_twin = face[twin]
+
+ # mask: 1.0 for valid edges, 0.0 for boundary/self
+ mask = (face != face_twin).astype(jnp.float32)
+
+ A = jnp.zeros((n_faces, n_faces))
+
+ # scatter with mask weighting (no boolean indexing)
+ A = A.at[face, face_twin].add(mask)
+ A = A.at[face_twin, face].add(mask)
+
+ # binarize (avoid double counts)
+ A = jnp.clip(A, 0.0, 1.0)
+
+ return A
+
+ # structure metric (1-hop + 2-hop)
+ def structure_matrix(A: Array) -> Array:
+ # A2 = A @ A
+ # normalize to avoid scaling issues
+ # return 2.0 - A - 0.5 * A2
+ return 2.0 - A
+
+ def get_face_vertices(
+ he_start: Array, heTable: Array, vertTable: Array, max_edges: int = 20
+ ) -> tuple[Array, Array]:
+ """Collect vertices of one face using half-edge traversal.
+
+ Returns fixed-size array (max_edges, 2) + mask
+ """
+ verts = jnp.zeros((max_edges, 2))
+ mask = jnp.zeros((max_edges,))
+ offset = jnp.array([0, 0])
+
+ def body_fun(i: int, state: tuple[Array, Array, Array, Array]) -> tuple[Array, Array, Array, Array]:
+ he, verts, mask, offset = state
+
+ source = heTable[he, 3].astype(jnp.int32)
+ off = heTable[he, 6:8]
+
+ pos = vertTable[source] + offset * L_box # jnp.array([width, height])
+
+ verts = verts.at[i].set(pos)
+ mask = mask.at[i].set(1.0)
+
+ offset += off
+ he_next = heTable[he, 1].astype(jnp.int32)
+
+ return (he_next, verts, mask, offset)
+
+ _he_final, verts, mask, offset = fori_loop(0, max_edges, body_fun, (he_start, verts, mask, offset))
+
+ return verts, mask
+
+ def polygon_centroid(verts: Array, mask: Array) -> Array:
+ """Compute centroid from masked polygon vertices."""
+ # shift for edges
+ v = verts
+ v_next = jnp.roll(v, -1, axis=0)
+
+ cross = v[:, 0] * v_next[:, 1] - v_next[:, 0] * v[:, 1]
+ cross = cross * mask
+
+ area = jnp.sum(cross) / 2.0 + 1e-12
+
+ cx = jnp.sum((v[:, 0] + v_next[:, 0]) * cross) / (6 * area)
+ cy = jnp.sum((v[:, 1] + v_next[:, 1]) * cross) / (6 * area)
+
+ return jnp.array([cx, cy])
+
+ def compute_face_centroids(faceTable: Array, heTable: Array, vertTable: Array) -> Array:
+ """Compute centroids for all faces."""
+ he_start = faceTable[:].astype(jnp.int32)
+
+ def single_face(he: Array) -> Array:
+ verts, mask = get_face_vertices(he, heTable, vertTable)
+ return polygon_centroid(verts, mask)
+
+ return vmap(single_face)(he_start)
+
+ alpha = 0.0
+ """ATTENTION !!!!!!!!!!!!"""
+
+ # face centroids
+ X = compute_face_centroids(faceTable, heTable, vertTable)
+ Y = compute_face_centroids(faceTable_target, heTable_target, vertTable_target)
+
+ N = X.shape[0]
+ M = Y.shape[0]
+
+ # uniform weights
+ a = jnp.ones(N) / N
+ b = jnp.ones(M) / M
+
+ # geometry cost
+ C_geom = l2(X, Y)
+
+ # structure cost
+ A1 = build_dual_adj(heTable, N)
+ A2 = build_dual_adj(heTable_target, M)
+
+ S1 = structure_matrix(A1)
+ S2 = structure_matrix(A2)
+
+ # project structure into pairwise node cost
+ # (cheap approximation of tem)
+ # C_l2 = l2(S1, S2)
+ C_mse = mse(S1, S2)
+
+ # combined cost
+ C_total = alpha * C_geom + (1.0 - alpha) * C_mse
+
+ # transport
+ gamma = sinkhorn(a, b, C_total)
+
+ # final loss
+ loss = jnp.sum(gamma * C_total)
+
+ return loss
+
+
+def cost_d_IAS( # noqa: N802
+ _vertTable: Array,
+ heTable: Array,
+ faceTable: Array,
+ _width: float,
+ _height: float,
+ _vertTable_target: Array,
+ heTable_target: Array,
+ faceTable_target: Array,
+ _selected_verts: Array | None = None,
+ _selected_hes: Array | None = None,
+ _selected_faces: Array | None = None,
+ _image_target: Array | None = None,
+) -> int:
+ r"""Discrete Index Aware Structural loss. Counts mismatched edges.
+
+ C_{d-IAS}(i,j) = \sum_{k=1}^{N} |A_1(i,k) - A_2(j,k)|
+ """
+
+ def build_dual_adj_np(heTable: Array, n_faces: int) -> NDArray:
+ face = heTable[:, 5]
+ twin = heTable[:, 2]
+
+ face_twin = face[twin]
+ mask = (face != face_twin).astype(float)
+
+ A = np.zeros((n_faces, n_faces))
+
+ A[face, face_twin] += mask
+ A[face_twin, face] += mask
+
+ A = np.clip(A, 0.0, 1.0)
+ return A
+
+ n1 = len(faceTable)
+ n2 = len(faceTable_target)
+
+ if n1 != n2:
+ msg = "This version requires same number of faces"
+ raise ValueError(msg)
+
+ n = n1
+
+ A1 = build_dual_adj_np(heTable, n)
+ A2 = build_dual_adj_np(heTable_target, n)
+
+ # --- Step 1: node matching via Hungarian ---
+ # cost between nodes = L1 difference of adjacency rows
+ C = np.sum(np.abs(A1[:, None, :] - A2[None, :, :]), axis=-1)
+
+ row_ind, col_ind = linear_sum_assignment(C)
+
+ # permutation: face i in A1 ↔ face col_ind[i] in A2
+ P = np.zeros((n, n))
+ P[row_ind, col_ind] = 1.0
+
+ # --- Step 2: permute A2 ---
+ A2_perm = P @ A2 @ P.T
+
+ # --- Step 3: count edge mismatches ---
+ diff = np.abs(A1 - A2_perm)
+
+ # count each edge once
+ diff_upper = np.triu(diff, k=1)
+
+ loss = int(np.sum(diff_upper))
+
+ return loss
+
+
+@partial(jit, static_argnums=(3, 4))
+def cost_tem_halfedge(
+ vertTable: Array,
+ heTable: Array,
+ faceTable: Array,
+ width: float,
+ height: float,
+ vertTable_target: Array,
+ heTable_target: Array,
+ faceTable_target: Array,
+ _selected_verts: Array | None = None,
+ _selected_hes: Array | None = None,
+ _selected_faces: Array | None = None,
+ _image_target: Array | None = None,
+) -> Array:
+ """TEM-inspired loss using half-edge dual graph (cells)."""
+
+ # ---------- helpers ----------
+ def pairwise_periodic_distances(x: Array, y: Array) -> Array:
+ dx = x[:, None, 0] - y[None, :, 0]
+ dy = x[:, None, 1] - y[None, :, 1]
+
+ dx = jnp.minimum(jnp.abs(dx), width - jnp.abs(dx))
+ dy = jnp.minimum(jnp.abs(dy), height - jnp.abs(dy))
+
+ return jnp.sqrt(dx**2 + dy**2 + 1e-12)
+
+ def sinkhorn(a: Array, b: Array, C: Array, eps: float = 5e-2, n_iters: int = 50) -> Array:
+ K = jnp.exp(-C / eps)
+ u = jnp.ones_like(a)
+ v = jnp.ones_like(b)
+
+ def body(_: int, state: tuple[Array, Array]) -> tuple[Array, Array]:
+ u, v = state
+ u = a / (K @ v + 1e-12)
+ v = b / (K.T @ u + 1e-12)
+ return (u, v)
+
+ u, v = fori_loop(0, n_iters, body, (u, v))
+ return jnp.outer(u, v) * K
+
+ # ---------- dual graph ----------
+ def build_dual_adj(heTable: Array, n_faces: int) -> Array:
+ face = heTable[:, 5].astype(jnp.int32)
+ twin = heTable[:, 2].astype(jnp.int32)
+
+ face_twin = face[twin]
+
+ valid = face != face_twin
+ keep = valid & (face < face_twin)
+
+ weight = keep.astype(jnp.float32)
+
+ A = jnp.zeros((n_faces, n_faces))
+
+ A = A.at[face, face_twin].add(weight)
+ A = A.at[face_twin, face].add(weight)
+
+ return A
+
+ def structure_matrix(A: Array) -> Array:
+ # A2 = A @ A
+ # return 2.0 - A - 0.5 * A2
+ return 2.0 - A
+
+ # ---------- centroids ----------
+ # faceTable stores a half-edge index → get its source vertex
+ he_idx = faceTable[:, 0].astype(jnp.int32)
+
+ X = vertTable[heTable[he_idx, 3].astype(jnp.int32), :2]
+ Y = vertTable_target[heTable_target[faceTable_target[:, 0].astype(jnp.int32), 3].astype(jnp.int32), :2]
+
+ N = X.shape[0]
+ M = Y.shape[0]
+
+ a = jnp.ones(N) / N
+ b = jnp.ones(M) / M
+
+ # ---------- costs ----------
+ C_geom = pairwise_periodic_distances(X, Y)
+
+ A1 = build_dual_adj(heTable, N)
+ A2 = build_dual_adj(heTable_target, M)
+
+ S1 = structure_matrix(A1)
+ S2 = structure_matrix(A2)
+
+ # structure comparison (non-periodic, abstract space)
+ diff = S1[:, None, :] - S2[None, :, :]
+ C_struct = jnp.sqrt(jnp.sum(diff**2, axis=-1) + 1e-12)
+
+ alpha = 0.5
+ C_total = alpha * C_geom + (1.0 - alpha) * C_struct
+
+ gamma = sinkhorn(a, b, C_total)
+
+ return jnp.sum(gamma * C_total)
+
+
+@partial(jit, static_argnums=(3, 4))
+def cost_v2v_tem(
+ vertTable: Array,
+ heTable: Array,
+ faceTable: Array,
+ width: float,
+ height: float,
+ vertTable_target: Array,
+ heTable_target: Array,
+ faceTable_target: Array,
+ _selected_verts: Array | None = None,
+ _selected_hes: Array | None = None,
+ _selected_faces: Array | None = None,
+ _image_target: Array | None = None,
+) -> Array:
+ """Mix of cost_v2v and cost_tem_halfedge (with respective weights 0.99 and 0.01)."""
+ return 0.99 * cost_v2v(
+ vertTable,
+ heTable,
+ faceTable,
+ width,
+ height,
+ vertTable_target,
+ heTable_target,
+ faceTable_target,
+ selected_verts=None,
+ selected_hes=None,
+ selected_faces=None,
+ image_target=None,
+ ) + 0.01 * cost_tem_halfedge(
+ vertTable,
+ heTable,
+ faceTable,
+ width,
+ height,
+ vertTable_target,
+ heTable_target,
+ faceTable_target,
+ selected_verts=None,
+ selected_hes=None,
+ selected_faces=None,
+ image_target=None,
+ )
+
+
+@partial(jit, static_argnums=(3, 4))
+def cost_v2v_ias(
+ vertTable: Array,
+ heTable: Array,
+ faceTable: Array,
+ width: float,
+ height: float,
+ vertTable_target: Array,
+ heTable_target: Array,
+ faceTable_target: Array,
+ _selected_verts: Array | None = None,
+ _selected_hes: Array | None = None,
+ _selected_faces: Array | None = None,
+ _image_target: Array | None = None,
+) -> Array:
+ """Mix of cost_v2v and cost_IAS (with weight 0.6 and 0.4)."""
+ return 0.6 * cost_v2v(
+ vertTable,
+ heTable,
+ faceTable,
+ width,
+ height,
+ vertTable_target,
+ heTable_target,
+ faceTable_target,
+ selected_verts=None,
+ selected_hes=None,
+ selected_faces=None,
+ _image_target=None,
+ ) + 0.4 * cost_IAS(
+ vertTable,
+ heTable,
+ faceTable,
+ width,
+ height,
+ vertTable_target,
+ heTable_target,
+ faceTable_target,
+ selected_verts=None,
+ selected_hes=None,
+ selected_faces=None,
+ image_target=None,
+ )
diff --git a/src/vertax/energy.py b/src/vertax/energy.py
index 8c67d61..bc35ccd 100644
--- a/src/vertax/energy.py
+++ b/src/vertax/energy.py
@@ -1,4 +1,37 @@
-"""Energy related functions."""
+"""Energy related functions, used for the inner optimization.
+
+They can be user defined functions but they have to respect a strict signature.
+
+For a `PbcMesh` and `PbcBilevelOptimizer`, the energy function must have the following signature:
+ vertTable: Array,
+ heTable: Array,
+ faceTable: Array,
+ vert_params: Array,
+ he_params: Array,
+ face_params: Array,
+and return an Array (or float).
+
+The names can vary and you can give default parameters. But the number and type of parameters is important.
+You don't have to use every parameters but they all have to be here.
+An unused parameters can of course also have the type None.
+
+Same for `BoundedMesh` and `BoundedBilevelOptimizer`, but with a slightly different signature:
+ vertTable: Array,
+ angTable: Array,
+ heTable: Array,
+ faceTable: Array,
+ selected_verts: Array | None,
+ selected_hes: Array | None,
+ selected_faces: Array | None,
+ vert_params: Array,
+ he_params: Array,
+ face_params: Array,
+and return an Array (or float).
+
+Hopefully the variable names are self-explanatory.
+
+You can create a function with this signature exactly that uses also locally-accessible external variable if you want.
+"""
import jax
import jax.numpy as jnp
@@ -11,10 +44,10 @@
MAX_EDGES_IN_ANY_FACE = 20
-def cell_energy(
+def _cell_energy(
face: Array, face_param: Array, vertTable: Array, heTable: Array, faceTable: Array, width: float, height: float
) -> Array:
- """Example of a cell energy function."""
+ """E1 energy for `PbcMesh` for a given face. Elastic term on cell areas and shape factors."""
area = get_area(face, vertTable, heTable, faceTable, width, height, MAX_EDGES_IN_ANY_FACE)
perimeter = get_perimeter(face, vertTable, heTable, faceTable, width, height, MAX_EDGES_IN_ANY_FACE)
return ((area - 1) ** 2) + ((perimeter - face_param) ** 2)
@@ -28,10 +61,10 @@ def energy_shape_factor_homo(
height: float,
face_params: Array,
) -> Array:
- """Example of an energy function."""
+ """E1 energy where the shape factor is uniform (give only one face_params, it will be broadcasted)."""
def mapped_fn(face: Array, param: Array) -> Array:
- return cell_energy(face, param, vertTable, heTable, faceTable, width, height)
+ return _cell_energy(face, param, vertTable, heTable, faceTable, width, height)
face_params_broadcasted = jnp.broadcast_to(face_params, (len(faceTable), *face_params.shape[1:]))
cell_energies = vmap(mapped_fn)(jnp.arange(len(faceTable)), face_params_broadcasted)
@@ -47,17 +80,17 @@ def energy_shape_factor_hetero(
selected_faces: Array,
face_params: Array,
) -> Array:
- """Example of an energy function."""
+ """E1 energy where the shape factor depends on the cell."""
def mapped_fn(face: Array, param: Array) -> Array:
- return cell_energy(face, param, vertTable, heTable, faceTable, width, height)
+ return _cell_energy(face, param, vertTable, heTable, faceTable, width, height)
cell_energies = vmap(mapped_fn)(selected_faces, face_params[selected_faces])
# cell_energies = vmap(mapped_fn)(jnp.arange(len(faceTable)), face_params)
return jnp.sum(cell_energies)
-def area_part(
+def _area_part(
face: Array, face_param: Array, vertTable: Array, heTable: Array, faceTable: Array, width: float, height: float
) -> Array:
"""Part of an energy function."""
@@ -65,7 +98,7 @@ def area_part(
return (a - face_param) ** 2
-def hedge_part(
+def _hedge_part(
he: Array, he_param: Array, vertTable: Array, heTable: Array, faceTable: Array, width: float, height: float
) -> Array:
"""Part of an energy function."""
@@ -82,14 +115,14 @@ def energy_line_tensions(
he_params: Array,
face_params: Array,
) -> Array:
- """Example of an energy function."""
+ """E2 energy for PBC meshes, elastic penalty on cell areas and line tension term weighted by edge lengths."""
K_areas = 20
def mapped_areas_part(face: Array, face_param: Array) -> Array:
- return area_part(face, face_param, vertTable, heTable, faceTable, width, height)
+ return _area_part(face, face_param, vertTable, heTable, faceTable, width, height)
def mapped_hedges_part(he: Array, he_param: Array) -> Array:
- return hedge_part(he, he_param, vertTable, heTable, faceTable, width, height)
+ return _hedge_part(he, he_param, vertTable, heTable, faceTable, width, height)
areas_part = vmap(mapped_areas_part)(jnp.arange(len(faceTable)), face_params)
hedges_part = vmap(mapped_hedges_part)(jnp.arange(len(heTable)), he_params)
@@ -102,28 +135,28 @@ def mapped_hedges_part(he: Array, he_param: Array) -> Array:
# Bounded
# ==========
@jit
-def cell_area_energy(face: Array, vertTable: Array, angTable: Array, heTable: Array, faceTable: Array) -> Array:
+def _cell_area_energy(face: Array, vertTable: Array, angTable: Array, heTable: Array, faceTable: Array) -> Array:
"""Part of an energy function."""
area = get_area_bounded(face, vertTable, angTable, heTable, faceTable)
return (area - TARGET_AREA) ** 2
@jit
-def surface_edge_energy(edge: Array, tension: Array, vertTable: Array, angTable: Array, heTable: Array) -> Array:
+def _surface_edge_energy(edge: Array, tension: Array, vertTable: Array, angTable: Array, heTable: Array) -> Array:
"""Part of an energy function."""
length = get_surface_length(edge, vertTable, angTable, heTable)
return length * tension
@jit
-def inner_edge_energy(edge: Array, tension: Array, vertTable: Array, heTable: Array) -> Array:
+def _inner_edge_energy(edge: Array, tension: Array, vertTable: Array, heTable: Array) -> Array:
"""Part of an energy function."""
length = get_edge_length(edge, vertTable, heTable)
return length * tension
@jit
-def energy_bounded(
+def energy_line_tensions_bounded(
vertTable: Array,
angTable: Array,
heTable: Array,
@@ -135,7 +168,7 @@ def energy_bounded(
he_params: Array,
_face_params: Array,
) -> Array:
- """Base energy function for bounded meshes."""
+ """E2 energy for bounded meshes, elastic penalty on cell areas and line tension term weighted by edge lengths."""
num_faces = faceTable.shape[0]
faces = jnp.arange(num_faces)
num_edges = angTable.size
@@ -147,17 +180,17 @@ def energy_bounded(
he_params = jax.nn.sigmoid(he_params) + 1
def mapped_fn_area(face: Array) -> Array:
- return cell_area_energy(face, vertTable, angTable, heTable, faceTable)
+ return _cell_area_energy(face, vertTable, angTable, heTable, faceTable)
cell_area_energies = jnp.sum(vmap(mapped_fn_area)(faces))
def mapped_fn_inner(edge: Array, tension: Array) -> Array:
- return inner_edge_energy(edge, tension, vertTable, heTable)
+ return _inner_edge_energy(edge, tension, vertTable, heTable)
inner_edge_energies = jnp.sum(vmap(mapped_fn_inner)(unique_edges, he_params))
def mapped_fn_surface(edge: Array, tension: Array) -> Array:
- return surface_edge_energy(edge, tension, vertTable, angTable, heTable)
+ return _surface_edge_energy(edge, tension, vertTable, angTable, heTable)
surface_edge_energies = jnp.sum(vmap(mapped_fn_surface)(edges, jnp.repeat(he_params, 2)))
return 20 * cell_area_energies + inner_edge_energies + surface_edge_energies
diff --git a/src/vertax/mesh.py b/src/vertax/mesh.py
deleted file mode 100644
index 0981735..0000000
--- a/src/vertax/mesh.py
+++ /dev/null
@@ -1,89 +0,0 @@
-"""Abstract mesh module."""
-
-from __future__ import annotations
-
-from typing import Any, NoReturn, TypeVar
-
-import jax.numpy as jnp
-import optax
-from jax import Array
-
-from vertax.method_enum import BilevelOptimizationMethod
-
-T = TypeVar("T")
-
-
-class NoPublicConstructor(type):
- """Metaclass that ensures a private constructor.
-
- If a class uses this metaclass like this:
-
- class SomeClass(metaclass=NoPublicConstructor):
- pass
-
- If you try to instantiate your class (`SomeClass()`),
- a `TypeError` will be thrown.
- """
-
- def __call__(cls, *args, **kwargs) -> NoReturn: # noqa
- """Make it impossible to call with ClassName()."""
- msg = f"{cls.__module__}.{cls.__qualname__} has no public constructor"
- raise TypeError(msg)
-
- def _create(cls: type[T], *args: Any, **kwargs: Any) -> T: # noqa: ANN401
- return super().__call__(*args, **kwargs) # type: ignore
-
-
-class Mesh(metaclass=NoPublicConstructor):
- """Generic mesh structure."""
-
- def __init__(self) -> None:
- """Do nothing but create attributes. Do not call this."""
- self.vertices: Array = jnp.array([])
- self.edges: Array = jnp.array([])
- self.faces: Array = jnp.array([])
- self.width: float = 0
- self.height: float = 0
-
- self.vertices_params: Array = jnp.array([])
- self.edges_params: Array = jnp.array([])
- self.faces_params: Array = jnp.array([])
-
- self.vertices_target: Array = jnp.array([])
- self.edges_target: Array = jnp.array([])
- self.faces_target: Array = jnp.array([])
-
- self.image_target: Array = jnp.array([])
-
- self.bilevel_optimization_method: BilevelOptimizationMethod = (
- BilevelOptimizationMethod.AUTOMATIC_DIFFERENTIATION
- )
- self.beta: float = 0.01
-
- self.min_dist_T1: float = 0.005
- self.max_nb_iterations: int = 1000
- self.tolerance: float = 1e-4
- self.patience: int = 5
-
- self.inner_solver: optax.GradientTransformation = optax.sgd(learning_rate=0.01)
- self.outer_solver: optax.GradientTransformation = optax.adam(learning_rate=0.0001, nesterov=True)
-
- @property
- def nb_vertices(self) -> int:
- """Get the number of vertices of the mesh."""
- return len(self.vertices)
-
- @property
- def nb_edges(self) -> int:
- """Get the number of edges of the mesh."""
- return self.nb_half_edges // 2
-
- @property
- def nb_half_edges(self) -> int:
- """Get the number of half-edges of the mesh."""
- return len(self.edges)
-
- @property
- def nb_faces(self) -> int:
- """Get the number of faces of the mesh."""
- return len(self.faces)
diff --git a/src/vertax/meshes/bounded_mesh.py b/src/vertax/meshes/bounded_mesh.py
index 9a496d1..ea2cd54 100644
--- a/src/vertax/meshes/bounded_mesh.py
+++ b/src/vertax/meshes/bounded_mesh.py
@@ -1,4 +1,4 @@
-"""Bounded mesh with arc circles for boundary cells."""
+"""Bounded mesh are useful to represent finite tissue clusters with curved interfaces."""
from __future__ import annotations
@@ -22,212 +22,42 @@
class BoundedMesh(Mesh):
- """Bounded mesh with arc circles for boundary cells."""
+ """Bounded mesh with arc circles for boundary cells.
- def __init__(self) -> None:
- """Do not call the constructor."""
- super().__init__()
- self.angles: Array = jnp.array([])
-
- def save_mesh_txt(
- self,
- directory: str,
- vertices_filename: str = "vertTable.txt",
- angles_filename: str = "angTable.txt",
- edges_filename: str = "heTable.txt",
- faces_filename: str = "faceTable.txt",
- vertices_params_filename: str = "vertParamsTable.txt",
- edges_params_filename: str = "heParamsTable.txt",
- faces_params_filename: str = "faceParamsTable.txt",
- constants_filename: str = "constants.txt",
- ) -> None:
- """Save a mesh in separate text files that can be read by numpy.
-
- Only save the vertices, angles, edges and faces, not other parameters.
-
- Args:
- directory (str): Path to the directory where to save the files.
- vertices_filename (str, optional): Filename for the vertices table. Defaults to "vertTable.txt".
- angles_filename (str, optional): Filename for the angles table. Defaults to "angTable.txt".
- edges_filename (str, optional): Filename for the half-edges table. Defaults to "heTable.txt".
- faces_filename (str, optional): Filename for the faces table. Defaults to "faceTable.txt".
- vertices_params_filename (str, optional): Filename for the vertices parameters table.
- Defaults to "vertParamsTable.txt".
- edges_params_filename (str, optional): Filename for the half-edges parameters table.
- Defaults to "heParamsTable.txt".
- faces_params_filename (str, optional): Filename for the faces parameters table.
- Defaults to "faceParamsTable.txt".
- constants_filename (str, optional): Filename for width/height.
- Defaults to "constants.txt".
- """
- dirpath = Path(directory)
- dirpath.mkdir(parents=True, exist_ok=True)
- np.savetxt(dirpath / vertices_filename, self.vertices)
- np.savetxt(dirpath / angles_filename, self.angles)
- np.savetxt(dirpath / edges_filename, self.edges)
- np.savetxt(dirpath / faces_filename, self.faces)
- np.savetxt(dirpath / vertices_params_filename, self.vertices_params)
- np.savetxt(dirpath / edges_params_filename, self.edges_params)
- np.savetxt(dirpath / faces_params_filename, self.faces_params)
- with (dirpath / constants_filename).open("w") as f:
- f.write(f"{self.width} {self.height}")
-
- def save_mesh(self, path: str) -> None:
- """Save mesh to a file.
-
- All BoundedMesh data is saved.
-
- Args:
- path (str): Path to the saved file. The extension is .npz.
- """
- Path(path).parent.mkdir(parents=True, exist_ok=True)
- np.savez_compressed(
- path,
- allow_pickle=False,
- vertices=self.vertices,
- edges=self.edges,
- faces=self.faces,
- angles=self.angles,
- width=self.width,
- height=self.height,
- vertices_params=self.vertices_params,
- edges_params=self.edges_params,
- faces_params=self.faces_params,
- )
-
- @classmethod
- def load_mesh(cls, path: str) -> Self:
- """Load a mesh from a file.
-
- All BoundedMesh data is reloaded.
-
- Args:
- path (str): Path to the mesh file (.npz).
-
- Returns:
- Mesh: the mesh loaded from the .npz file.
- """
- mesh_file = np.load(path)
- mesh = cls._create()
- mesh.vertices, mesh.edges, mesh.faces, mesh.angles = (
- mesh_file["vertices"],
- mesh_file["edges"],
- mesh_file["faces"],
- mesh_file["angles"],
- )
- mesh.width = mesh_file["width"]
- mesh.height = mesh_file["height"]
- mesh.vertices_params = mesh_file["vertices_params"]
- mesh.edges_params = mesh_file["edges_params"]
- mesh.faces_params = mesh_file["faces_params"]
- return mesh
+ For a BoundedMesh, `vertices` is a 2D array of floats of size (nb_vertices, 2) ;
+ with the coordinates of the vertices (in ]0, width[ x ]0, height[ ).
- @classmethod
- def load_mesh_txt(
- cls,
- directory: str,
- vertices_filename: str = "vertTable.txt",
- angles_filename: str = "angTable.txt",
- edges_filename: str = "heTable.txt",
- faces_filename: str = "faceTable.txt",
- vertices_params_filename: str = "vertParamsTable.txt",
- edges_params_filename: str = "heParamsTable.txt",
- faces_params_filename: str = "faceParamsTable.txt",
- constants_filename: str = "constants.txt",
- ) -> Self:
- """Load a mesh from text files.
+ `edges` is a 2D array of integers of size (nb_half_edges, 8), with:
- Only load the vertices, angles, edges and faces, not other parameters.
+ - id of previous half-edge,
+ - id of next half-edge,
+ - id of twin half-edge,
+ - id of source vertex + 2 if current half-edge is an inside edge, else 0,
+ - id of target vertex + 2 if current half-edge is an inside edge, else 1,
+ - id of source vertex + 2 if current half-edge is an outside edge, else 0,
+ - id of target vertex + 2 if current half-edge is an outside edge, else 1,
+ - id of the face containing the half-edge.
- Args:
- directory (str): Directory where the text files are stored.
- vertices_filename (str, optional): Filename for the vertices table. Defaults to "vertTable.txt".
- angles_filename (str, optional): Filename for the angles table. Defaults to "angTable.txt".
- edges_filename (str, optional): Filename for the half-edges table. Defaults to "heTable.txt".
- faces_filename (str, optional): Filename for the faces table. Defaults to "faceTable.txt".
- vertices_params_filename (str, optional): Filename for the vertices parameters table.
- Defaults to "vertParamsTable.txt".
- edges_params_filename (str, optional): Filename for the half-edges parameters table.
- Defaults to "heParamsTable.txt".
- faces_params_filename (str, optional): Filename for the faces parameters table.
- Defaults to "faceParamsTable.txt".
- constants_filename (str, optional): Filename for width/height/MAX_EDGES_IN_ANY_FACE.
- Defaults to "constants.txt".
-
- Returns:
- Self: The loaded mesh.
- """
- dirpath = Path(directory)
- dirpath.mkdir(parents=True, exist_ok=True)
+ `faces` is a 1D array of integers of size (nb_faces) containing the id of a half-edge belonging to this face.
- mesh = cls._create()
- mesh.vertices = jnp.array(np.loadtxt(dirpath / vertices_filename, dtype=np.float64))
- mesh.angles = jnp.array(np.loadtxt(dirpath / angles_filename, dtype=np.float64))
- mesh.edges = jnp.array(np.loadtxt(dirpath / edges_filename, dtype=np.int64))
- mesh.faces = jnp.array(np.loadtxt(dirpath / faces_filename, dtype=np.int64))
- mesh.vertices_params = jnp.array(np.loadtxt(dirpath / vertices_params_filename, dtype=np.float64))
- mesh.edges_params = jnp.array(np.loadtxt(dirpath / edges_params_filename, dtype=np.int64))
- mesh.faces_params = jnp.array(np.loadtxt(dirpath / faces_params_filename, dtype=np.int64))
- with (dirpath / constants_filename).open("r") as f:
- numbers = f.readline().split()
- mesh.width = float(numbers[0])
- mesh.height = float(numbers[1])
- return mesh
+ `angles` is a 1D array of floats of size (nb_angles), with the angles sustaining the arcs of the free interfaces ;
+ between 0 and PI / 2.
+ """
- @classmethod
- def copy_mesh(cls, other_mesh: Self) -> Self:
- """Copy all parameters from another mesh in a new mesh."""
- mesh = cls._create()
- mesh.vertices = other_mesh.vertices.copy()
- mesh.edges = other_mesh.edges.copy()
- mesh.faces = other_mesh.faces.copy()
- mesh.angles = other_mesh.angles.copy()
- mesh.width = other_mesh.width
- mesh.height = other_mesh.height
- mesh.vertices_params = other_mesh.vertices_params.copy()
- mesh.edges_params = other_mesh.edges_params.copy()
- mesh.faces_params = other_mesh.faces_params.copy()
-
- return mesh
+ def __init__(self) -> None:
+ """Do not call the constructor."""
+ super().__init__()
+ self.angles: Array = jnp.array([])
+ """Angle sustaining the arced free interfaces. Between 0 and PI / 2."""
@property
def nb_angles(self) -> int:
- """Get the number of angles of the mesh."""
+ """Get the number of angles (free interfaces) of the mesh."""
return len(self.angles)
- def get_length(self, half_edge_id: Array) -> Array:
- """Get the length of an edge."""
- vertTable = jnp.vstack([jnp.array([[0.0, 0.0], [1.0, 1.0]]), self.vertices])
- angTable = jnp.repeat(self.angles, 2)
-
- def _get_length(half_edge_id: Array) -> Array:
- return get_any_length(half_edge_id, vertTable, angTable, self.edges)
-
- return jax.vmap(_get_length)(half_edge_id)
-
- def get_perimeter(self, face_id: Array) -> Array:
- """Get the area of a face."""
- vertTable = jnp.vstack([jnp.array([[0.0, 0.0], [1.0, 1.0]]), self.vertices])
- angTable = jnp.repeat(self.angles, 2)
-
- def _get_perimeter(face_id: Array) -> Array:
- return get_perimeter_bounded(face_id, vertTable, angTable, self.edges, self.faces)
-
- return jax.vmap(_get_perimeter)(face_id)
-
- def get_area(self, face_id: Array) -> Array:
- """Get the area of a face."""
- vertTable = jnp.vstack([jnp.array([[0.0, 0.0], [1.0, 1.0]]), self.vertices])
- angTable = jnp.repeat(self.angles, 2)
-
- def _get_area(face_id: Array) -> Array:
- return get_area_bounded(face_id, vertTable, angTable, self.edges, self.faces)
-
- return jax.vmap(_get_area)(face_id)
-
@classmethod
def from_random_seeds(cls, nb_seeds: int, width: float, height: float, random_key: int, nb_fates: int = 2) -> Self:
- """Create a bounded Mesh from random seeds.
+ """Create a bounded Mesh from random seeds, based on a Voronoi diagram with arced free interfaces.
Args:
nb_seeds (int): Number of random seeds to use.
@@ -245,7 +75,7 @@ def from_random_seeds(cls, nb_seeds: int, width: float, height: float, random_ke
@classmethod
def from_seeds(cls, seeds: NDArray, width: float, height: float, random_key: int, nb_fates: int = 2) -> Self: # noqa: C901
- """Create a bounded Mesh from a list of seeds.
+ """Create a bounded Mesh from a list of given seeds.
The seeds are assumed to have x-coordinate in ]0, width[ and y-coordinate in ]0, height[.
Note that the final mesh might not use your seeds if they don't work to create a correct
@@ -485,6 +315,202 @@ def from_seeds(cls, seeds: NDArray, width: float, height: float, random_key: int
else (width, height) * rng.random((n_cells, 2))
) # type: ignore
+ @classmethod
+ def create_empty(cls) -> Self:
+ """Create an empty mesh. Use if you know what you're doing !"""
+ return cls._create()
+
+ @classmethod
+ def copy_mesh(cls, other_mesh: Self) -> Self:
+ """Copy all parameters from another mesh in a new mesh."""
+ mesh = cls._create()
+ mesh.vertices = other_mesh.vertices.copy()
+ mesh.edges = other_mesh.edges.copy()
+ mesh.faces = other_mesh.faces.copy()
+ mesh.angles = other_mesh.angles.copy()
+ mesh.width = other_mesh.width
+ mesh.height = other_mesh.height
+ mesh.vertices_params = other_mesh.vertices_params.copy()
+ mesh.edges_params = other_mesh.edges_params.copy()
+ mesh.faces_params = other_mesh.faces_params.copy()
+
+ return mesh
+
+ def save_mesh(self, path: str) -> None:
+ """Save mesh to a file.
+
+ All BoundedMesh data is saved.
+
+ Args:
+ path (str): Path to the saved file. The extension is .npz.
+ """
+ Path(path).parent.mkdir(parents=True, exist_ok=True)
+ np.savez_compressed(
+ path,
+ allow_pickle=False,
+ vertices=self.vertices,
+ edges=self.edges,
+ faces=self.faces,
+ angles=self.angles,
+ width=self.width,
+ height=self.height,
+ vertices_params=self.vertices_params,
+ edges_params=self.edges_params,
+ faces_params=self.faces_params,
+ )
+
+ @classmethod
+ def load_mesh(cls, path: str) -> Self:
+ """Load a mesh from a file.
+
+ All BoundedMesh data is reloaded.
+
+ Args:
+ path (str): Path to the mesh file (.npz).
+
+ Returns:
+ Mesh: the mesh loaded from the .npz file.
+ """
+ mesh_file = np.load(path)
+ mesh = cls._create()
+ mesh.vertices, mesh.edges, mesh.faces, mesh.angles = (
+ mesh_file["vertices"],
+ mesh_file["edges"],
+ mesh_file["faces"],
+ mesh_file["angles"],
+ )
+ mesh.width = mesh_file["width"]
+ mesh.height = mesh_file["height"]
+ mesh.vertices_params = mesh_file["vertices_params"]
+ mesh.edges_params = mesh_file["edges_params"]
+ mesh.faces_params = mesh_file["faces_params"]
+ return mesh
+
+ def save_mesh_txt(
+ self,
+ directory: str,
+ vertices_filename: str = "vertTable.txt",
+ angles_filename: str = "angTable.txt",
+ edges_filename: str = "heTable.txt",
+ faces_filename: str = "faceTable.txt",
+ vertices_params_filename: str = "vertParamsTable.txt",
+ edges_params_filename: str = "heParamsTable.txt",
+ faces_params_filename: str = "faceParamsTable.txt",
+ constants_filename: str = "constants.txt",
+ ) -> None:
+ """Save a mesh in separate text files that can be read by numpy.
+
+ Only save the vertices, angles, edges and faces, not other parameters.
+
+ Args:
+ directory (str): Path to the directory where to save the files.
+ vertices_filename (str, optional): Filename for the vertices table. Defaults to "vertTable.txt".
+ angles_filename (str, optional): Filename for the angles table. Defaults to "angTable.txt".
+ edges_filename (str, optional): Filename for the half-edges table. Defaults to "heTable.txt".
+ faces_filename (str, optional): Filename for the faces table. Defaults to "faceTable.txt".
+ vertices_params_filename (str, optional): Filename for the vertices parameters table.
+ Defaults to "vertParamsTable.txt".
+ edges_params_filename (str, optional): Filename for the half-edges parameters table.
+ Defaults to "heParamsTable.txt".
+ faces_params_filename (str, optional): Filename for the faces parameters table.
+ Defaults to "faceParamsTable.txt".
+ constants_filename (str, optional): Filename for width/height.
+ Defaults to "constants.txt".
+ """
+ dirpath = Path(directory)
+ dirpath.mkdir(parents=True, exist_ok=True)
+ np.savetxt(dirpath / vertices_filename, self.vertices)
+ np.savetxt(dirpath / angles_filename, self.angles)
+ np.savetxt(dirpath / edges_filename, self.edges)
+ np.savetxt(dirpath / faces_filename, self.faces)
+ np.savetxt(dirpath / vertices_params_filename, self.vertices_params)
+ np.savetxt(dirpath / edges_params_filename, self.edges_params)
+ np.savetxt(dirpath / faces_params_filename, self.faces_params)
+ with (dirpath / constants_filename).open("w") as f:
+ f.write(f"{self.width} {self.height}")
+
+ @classmethod
+ def load_mesh_txt(
+ cls,
+ directory: str,
+ vertices_filename: str = "vertTable.txt",
+ angles_filename: str = "angTable.txt",
+ edges_filename: str = "heTable.txt",
+ faces_filename: str = "faceTable.txt",
+ vertices_params_filename: str = "vertParamsTable.txt",
+ edges_params_filename: str = "heParamsTable.txt",
+ faces_params_filename: str = "faceParamsTable.txt",
+ constants_filename: str = "constants.txt",
+ ) -> Self:
+ """Load a mesh from text files.
+
+ Only load the vertices, angles, edges and faces, not other parameters.
+
+ Args:
+ directory (str): Directory where the text files are stored.
+ vertices_filename (str, optional): Filename for the vertices table. Defaults to "vertTable.txt".
+ angles_filename (str, optional): Filename for the angles table. Defaults to "angTable.txt".
+ edges_filename (str, optional): Filename for the half-edges table. Defaults to "heTable.txt".
+ faces_filename (str, optional): Filename for the faces table. Defaults to "faceTable.txt".
+ vertices_params_filename (str, optional): Filename for the vertices parameters table.
+ Defaults to "vertParamsTable.txt".
+ edges_params_filename (str, optional): Filename for the half-edges parameters table.
+ Defaults to "heParamsTable.txt".
+ faces_params_filename (str, optional): Filename for the faces parameters table.
+ Defaults to "faceParamsTable.txt".
+ constants_filename (str, optional): Filename for width/height/MAX_EDGES_IN_ANY_FACE.
+ Defaults to "constants.txt".
+
+ Returns:
+ Self: The loaded mesh.
+ """
+ dirpath = Path(directory)
+ dirpath.mkdir(parents=True, exist_ok=True)
+
+ mesh = cls._create()
+ mesh.vertices = jnp.array(np.loadtxt(dirpath / vertices_filename, dtype=np.float64))
+ mesh.angles = jnp.array(np.loadtxt(dirpath / angles_filename, dtype=np.float64))
+ mesh.edges = jnp.array(np.loadtxt(dirpath / edges_filename, dtype=np.int64))
+ mesh.faces = jnp.array(np.loadtxt(dirpath / faces_filename, dtype=np.int64))
+ mesh.vertices_params = jnp.array(np.loadtxt(dirpath / vertices_params_filename, dtype=np.float64))
+ mesh.edges_params = jnp.array(np.loadtxt(dirpath / edges_params_filename, dtype=np.int64))
+ mesh.faces_params = jnp.array(np.loadtxt(dirpath / faces_params_filename, dtype=np.int64))
+ with (dirpath / constants_filename).open("r") as f:
+ numbers = f.readline().split()
+ mesh.width = float(numbers[0])
+ mesh.height = float(numbers[1])
+ return mesh
+
+ def get_length(self, half_edge_id: Array) -> Array:
+ """Get the length of an edge."""
+ vertTable = jnp.vstack([jnp.array([[0.0, 0.0], [1.0, 1.0]]), self.vertices])
+ angTable = jnp.repeat(self.angles, 2)
+
+ def _get_length(half_edge_id: Array) -> Array:
+ return get_any_length(half_edge_id, vertTable, angTable, self.edges)
+
+ return jax.vmap(_get_length)(half_edge_id)
+
+ def get_perimeter(self, face_id: Array) -> Array:
+ """Get the area of a face."""
+ vertTable = jnp.vstack([jnp.array([[0.0, 0.0], [1.0, 1.0]]), self.vertices])
+ angTable = jnp.repeat(self.angles, 2)
+
+ def _get_perimeter(face_id: Array) -> Array:
+ return get_perimeter_bounded(face_id, vertTable, angTable, self.edges, self.faces)
+
+ return jax.vmap(_get_perimeter)(face_id)
+
+ def get_area(self, face_id: Array) -> Array:
+ """Get the area of a face."""
+ vertTable = jnp.vstack([jnp.array([[0.0, 0.0], [1.0, 1.0]]), self.vertices])
+ angTable = jnp.repeat(self.angles, 2)
+
+ def _get_area(face_id: Array) -> Array:
+ return get_area_bounded(face_id, vertTable, angTable, self.edges, self.faces)
+
+ return jax.vmap(_get_area)(face_id)
+
def _fate_selection(faceTable: NDArray, n_fates: int, rng: Generator) -> NDArray:
n_cells = faceTable.size
diff --git a/src/vertax/meshes/mesh.py b/src/vertax/meshes/mesh.py
index 985a605..5ff6091 100644
--- a/src/vertax/meshes/mesh.py
+++ b/src/vertax/meshes/mesh.py
@@ -34,24 +34,60 @@ def _create(cls: type[T], *args: Any, **kwargs: Any) -> T: # noqa: ANN401
class Mesh(metaclass=NoPublicConstructor):
- """Generic mesh structure."""
+ """Generic mesh structure. It is an abstract base class, not to be used directly.
+
+ It defines common attributes and functions between `PbcMesh` and `BoundedMesh`.
+ """
def __init__(self) -> None:
- """Do nothing but create attributes. Do not call this."""
- self.vertices: Array = jnp.array([])
- self.edges: Array = jnp.array([])
+ """Do nothing but create attributes. Do not call this, but call specialized class methods to create meshes.
+
+ See `PbcMesh` and `BoundedMesh`.
+
+ Technically, it uses a DCEL structure.
+ """
self.faces: Array = jnp.array([])
+ """The cells of the tissue."""
+ self.edges: Array = jnp.array([])
+ """The interface between cells. Technically half-edges."""
+ self.vertices: Array = jnp.array([])
+ """The mesh vertices, where cells meet."""
self.width: float = 0
+ """The mesh live in a rectangle of size [0, width] in the X direction."""
self.height: float = 0
+ """The mesh live in a rectangle of size [0, height] in the Y direction."""
- self.vertices_params: Array = jnp.array([])
- self.edges_params: Array = jnp.array([])
self.faces_params: Array = jnp.array([])
+ """Parameters attached to faces. Can be optimized."""
+ self.edges_params: Array = jnp.array([])
+ """Parameters attached to edges. Can be optimized."""
+ self.vertices_params: Array = jnp.array([])
+ """Parameters attached to vertices. Can be optimized."""
+
+ @property
+ def nb_faces(self) -> int:
+ """Get the number of faces of the mesh."""
+ return len(self.faces)
+
+ @property
+ def nb_edges(self) -> int:
+ """Get the number of edges of the mesh."""
+ return self.nb_half_edges // 2
+
+ @property
+ def nb_half_edges(self) -> int:
+ """Get the number of half-edges of the mesh, ie. twice the number of edges."""
+ return len(self.edges)
+
+ @property
+ def nb_vertices(self) -> int:
+ """Get the number of vertices of the mesh."""
+ return len(self.vertices)
def save_mesh(self, path: str) -> None:
"""Save mesh to a file.
- All mesh data is saved. Must be implemented by child classes.
+ All mesh data is saved.
Args:
path (str): Path to the saved file. The extension is .npz.
@@ -62,32 +98,10 @@ def save_mesh(self, path: str) -> None:
def load_mesh(cls, path: str) -> Self:
"""Load a mesh from a file.
- Must be implemented by child classes.
-
Args:
path (str): Path to the mesh file (.npz).
Returns:
- Mesh: the mesh loaded from the .npz file.
+ The mesh loaded from the .npz file.
"""
raise NotImplementedError
-
- @property
- def nb_vertices(self) -> int:
- """Get the number of vertices of the mesh."""
- return len(self.vertices)
-
- @property
- def nb_edges(self) -> int:
- """Get the number of edges of the mesh."""
- return self.nb_half_edges // 2
-
- @property
- def nb_half_edges(self) -> int:
- """Get the number of half-edges of the mesh."""
- return len(self.edges)
-
- @property
- def nb_faces(self) -> int:
- """Get the number of faces of the mesh."""
- return len(self.faces)
diff --git a/src/vertax/meshes/pbc_mesh.py b/src/vertax/meshes/pbc_mesh.py
index e47dd12..10b65cc 100644
--- a/src/vertax/meshes/pbc_mesh.py
+++ b/src/vertax/meshes/pbc_mesh.py
@@ -1,4 +1,4 @@
-"""Periodic Boundary Condition on a mesh."""
+"""Periodic Boundary Condition on a mesh, useful for bulk tissue dynamics with no explicit boundaries."""
from pathlib import Path
from typing import Self
@@ -18,210 +18,42 @@
class PbcMesh(Mesh):
- """Periodic Boundary Condition on a mesh."""
+ """Periodic Boundary Condition on a mesh.
- def __init__(self) -> None:
- """Do not call the constructor."""
- super().__init__()
-
- self.MAX_EDGES_IN_ANY_FACE: int = 20
-
- @classmethod
- def copy_mesh(cls, other_mesh: Self) -> Self:
- """Copy all parameters from another mesh in a new mesh."""
- mesh = cls._create()
- mesh.vertices = other_mesh.vertices.copy()
- mesh.edges = other_mesh.edges.copy()
- mesh.faces = other_mesh.faces.copy()
- mesh.width = other_mesh.width
- mesh.height = other_mesh.height
- mesh.vertices_params = other_mesh.vertices_params.copy()
- mesh.edges_params = other_mesh.edges_params.copy()
- mesh.faces_params = other_mesh.faces_params.copy()
- mesh.MAX_EDGES_IN_ANY_FACE = other_mesh.MAX_EDGES_IN_ANY_FACE
+ For a PbcMesh, `vertices` is a 2D array of floats of size (nb_vertices, 2) ;
+ with the coordinates of the vertices (in ]0, width[ x ]0, height[ ).
- return mesh
-
- def save_mesh_txt(
- self,
- directory: str,
- vertices_filename: str = "vertTable.txt",
- edges_filename: str = "heTable.txt",
- faces_filename: str = "faceTable.txt",
- vertices_params_filename: str = "vertParamsTable.txt",
- edges_params_filename: str = "heParamsTable.txt",
- faces_params_filename: str = "faceParamsTable.txt",
- constants_filename: str = "constants.txt",
- ) -> None:
- """Save a mesh in separate text files that can be read by numpy.
+ `edges` is a 2D array of integers of size (nb_half_edges, 8), with:
- Only save the vertices, edges and faces, not other parameters.
+ - id of previous half-edge,
+ - id of next half-edge,
+ - id of twin half-edge,
+ - id of source vertex,
+ - id of target vertex,
+ - id of the face containing the half-edge.
+ - x-offset (either -1, 0 or 1) depending on the target vertex crossing a boundary.
+ - y-offset (either -1, 0 or 1) depending on the target vertex crossing a boundary.
- Args:
- directory (str): Path to the directory where to save the files.
- vertices_filename (str, optional): Filename for the vertices table. Defaults to "vertTable.txt".
- edges_filename (str, optional): Filename for the half-edges table. Defaults to "heTable.txt".
- faces_filename (str, optional): Filename for the faces table. Defaults to "faceTable.txt".
- vertices_params_filename (str, optional): Filename for the vertices parameters table.
- Defaults to "vertParamsTable.txt".
- edges_params_filename (str, optional): Filename for the half-edges parameters table.
- Defaults to "heParamsTable.txt".
- faces_params_filename (str, optional): Filename for the faces parameters table.
- Defaults to "faceParamsTable.txt".
- constants_filename (str, optional): Filename for width/height/MAX_EDGES_IN_ANY_FACE.
- Defaults to "constants.txt".
- """
- dirpath = Path(directory)
- dirpath.mkdir(parents=True, exist_ok=True)
- np.savetxt(dirpath / vertices_filename, self.vertices)
- np.savetxt(dirpath / edges_filename, self.edges)
- np.savetxt(dirpath / faces_filename, self.faces)
- np.savetxt(dirpath / vertices_params_filename, self.vertices_params)
- np.savetxt(dirpath / edges_params_filename, self.edges_params)
- np.savetxt(dirpath / faces_params_filename, self.faces_params)
- with (dirpath / constants_filename).open("w") as f:
- f.write(f"{self.width} {self.height} {self.MAX_EDGES_IN_ANY_FACE}")
-
- def save_mesh(self, path: str) -> None:
- """Save mesh to a file.
-
- All PBCMesh data is saved.
-
- Args:
- path (str): Path to the saved file. The extension is .npz, a numpy format.
- """
- Path(path).parent.mkdir(parents=True, exist_ok=True)
- np.savez_compressed(
- path,
- allow_pickle=False,
- vertices=self.vertices,
- edges=self.edges,
- faces=self.faces,
- width=self.width,
- height=self.height,
- vertices_params=self.vertices_params,
- edges_params=self.edges_params,
- faces_params=self.faces_params,
- MAX_EDGES_IN_ANY_FACE=self.MAX_EDGES_IN_ANY_FACE,
- )
-
- @classmethod
- def load_mesh(cls, path: str) -> Self:
- """Load a mesh from a file.
-
- All PBCMesh data is reloaded.
-
- Args:
- path (str): Path to the mesh file (.npz), numpy format.
-
- Returns:
- Mesh: the mesh loaded from the numpy .npz file.
- """
- mesh_file = np.load(path)
- mesh = cls._create()
- mesh.vertices, mesh.edges, mesh.faces = mesh_file["vertices"], mesh_file["edges"], mesh_file["faces"]
- mesh.width, mesh.height = mesh_file["width"], mesh_file["height"]
- mesh.vertices_params = mesh_file["vertices_params"]
- mesh.edges_params = mesh_file["edges_params"]
- mesh.faces_params = mesh_file["faces_params"]
- mesh.MAX_EDGES_IN_ANY_FACE = mesh_file["MAX_EDGES_IN_ANY_FACE"]
- return mesh
-
- @classmethod
- def load_mesh_txt(
- cls,
- directory: str,
- vertices_filename: str = "vertTable.txt",
- edges_filename: str = "heTable.txt",
- faces_filename: str = "faceTable.txt",
- vertices_params_filename: str = "vertParamsTable.txt",
- edges_params_filename: str = "heParamsTable.txt",
- faces_params_filename: str = "faceParamsTable.txt",
- constants_filename: str = "constants.txt",
- ) -> Self:
- """Load a mesh from text files.
-
- Only load the vertices, edges and faces, not other parameters.
-
- Args:
- directory (str): Directory where the text files are stored.
- vertices_filename (str, optional): Filename for the vertices table. Defaults to "vertTable.txt".
- edges_filename (str, optional): Filename for the half-edges table. Defaults to "heTable.txt".
- faces_filename (str, optional): Filename for the faces table. Defaults to "faceTable.txt".
- vertices_params_filename (str, optional): Filename for the vertices parameters table.
- Defaults to "vertParamsTable.txt".
- edges_params_filename (str, optional): Filename for the half-edges parameters table.
- Defaults to "heParamsTable.txt".
- faces_params_filename (str, optional): Filename for the faces parameters table.
- Defaults to "faceParamsTable.txt".
- constants_filename (str, optional): Filename for width/height/MAX_EDGES_IN_ANY_FACE.
- Defaults to "constants.txt".
-
- Returns:
- Self: The loaded mesh.
- """
- dirpath = Path(directory)
- dirpath.mkdir(parents=True, exist_ok=True)
-
- mesh = cls._create()
- mesh.vertices = jnp.array(np.loadtxt(dirpath / vertices_filename, dtype=np.float64))
- mesh.edges = jnp.array(np.loadtxt(dirpath / edges_filename, dtype=np.int64))
- mesh.faces = jnp.array(np.loadtxt(dirpath / faces_filename, dtype=np.int64))
- mesh.vertices_params = jnp.array(np.loadtxt(dirpath / vertices_params_filename, dtype=np.float64))
- mesh.edges_params = jnp.array(np.loadtxt(dirpath / edges_params_filename, dtype=np.int64))
- mesh.faces_params = jnp.array(np.loadtxt(dirpath / faces_params_filename, dtype=np.int64))
- with (dirpath / constants_filename).open("r") as f:
- numbers = f.readline().split()
- mesh.width = float(numbers[0])
- mesh.height = float(numbers[1])
- mesh.MAX_EDGES_IN_ANY_FACE = int(numbers[2])
- return mesh
-
- def get_length(self, half_edge_id: Array) -> Array:
- """Get the length of an edge."""
-
- def _get_length(half_edge_id: Array) -> Array:
- return get_length(half_edge_id, self.vertices, self.edges, self.faces, self.width, self.height)
-
- return jax.vmap(_get_length)(half_edge_id)
-
- def get_length_with_offset(self, half_edge_id: Array) -> Array:
- """Get the length of an edge along with its offsets in an array (length, offset x, offset y)."""
-
- def _get_length_with_offset(half_edge_id: Array) -> Array:
- return get_length_with_offset(half_edge_id, self.vertices, self.edges, self.faces, self.width, self.height)
-
- return jax.vmap(_get_length_with_offset)(half_edge_id)
-
- def get_perimeter(self, face_id: Array) -> Array:
- """Get the perimeter of a face."""
-
- def _get_perimeter(face_id: Array) -> Array:
- return get_perimeter(
- face_id, self.vertices, self.edges, self.faces, self.width, self.height, self.MAX_EDGES_IN_ANY_FACE
- )
-
- return jax.vmap(_get_perimeter)(face_id)
-
- def get_area(self, face_id: Array) -> Array:
- """Get the area of a face."""
-
- def _get_area(face_id: Array) -> Array:
- return get_area(
- face_id, self.vertices, self.edges, self.faces, self.width, self.height, self.MAX_EDGES_IN_ANY_FACE
- )
+ `faces` is a 1D array of integers of size (nb_faces) containing the id of a half-edge belonging to this face.
+ """
- return jax.vmap(_get_area)(face_id)
+ def __init__(self) -> None:
+ """Do not call the constructor directly, use the dedicated class methods such as:
+
+ - `from_random_seeds`,
+ - `from_seeds`,
+ - `from_image`,
+ - `from_mask`,
+ - `create_empty`.
+ """ # noqa: D415
+ super().__init__()
- def update_boundary_conditions(self) -> None:
- """Force periodic boundary conditions again after an update."""
- self.vertices, self.edges, self.faces = update_pbc(
- self.vertices, self.edges, self.faces, self.width, self.height
- )
+ self.MAX_EDGES_IN_ANY_FACE: int = 20
+ """Optimization parameter : must be more than the estimated maximum number of edges in a face. Base value is 20.""" # noqa: E501
@classmethod
- def periodic_voronoi_from_random_seeds(cls, nb_seeds: int, width: float, height: float, random_key: int) -> Self:
- """Create a Periodic Voronoi Mesh from random seeds.
+ def from_random_seeds(cls, nb_seeds: int, width: float, height: float, random_key: int) -> Self:
+ """Create a Periodic Boundary Conditions Mesh from a Voronoi diagram based on random seeds.
Args:
nb_seeds (int): Number of random seeds to use.
@@ -234,11 +66,11 @@ def periodic_voronoi_from_random_seeds(cls, nb_seeds: int, width: float, height:
"""
key = jax.random.PRNGKey(random_key)
seeds = jnp.array((width, height)) * jax.random.uniform(key, (nb_seeds, 2))
- return cls.periodic_voronoi_from_seeds(seeds, width, height)
+ return cls.from_seeds(seeds, width, height)
@classmethod
- def periodic_voronoi_from_seeds(cls, seeds: Array, width: float, height: float) -> Self:
- """Create a Periodic Voronoi Mesh from a list of seeds.
+ def from_seeds(cls, seeds: Array, width: float, height: float) -> Self:
+ """Create a Periodic Boundary Conditions Mesh from a Voronoi diagram based on a list of seeds.
The seeds are assumed to have positive x and y positions.
@@ -275,7 +107,7 @@ def periodic_voronoi_from_seeds(cls, seeds: Array, width: float, height: float)
return pbc_mesh
@classmethod
- def periodic_from_image(
+ def from_image(
cls,
image: NDArray,
) -> Self:
@@ -285,26 +117,30 @@ def periodic_from_image(
The result will probably be imperfect and it will always be better if you
provide directly a mask (with no holes) with the function "periodic_from_mask".
+ The mask is made periodic by mirroring its edges.
+
Args:
image (NDArray): The image which will act as a template for the mesh.
Returns:
- tuple[Array, Array, Array]: The vertices, half-edges and faces table of the mesh.
+ Self: The corresponding mesh.
"""
- return cls.periodic_from_mask(mask_from_image(image))
+ return cls.from_mask(mask_from_image(image))
@classmethod
- def periodic_from_mask( # noqa: C901
+ def from_mask( # noqa: C901
cls,
mask: NDArray,
) -> Self:
"""Create a rudimentary mesh with periodic boundary conditions from a mask with no holes.
+ The mask is made periodic by mirroring its edges.
+
Args:
mask (NDArray): The mask with no holes which will act as a template for the mesh.
Returns:
- tuple[Array, Array, Array]: The vertices, half-edges and faces table of the mesh.
+ Self: The corresponding mesh.
"""
padded_mask = pad(mask, save=False, output_path="refined_and_padded_image.tiff")
# Find vertices, edges, faces
@@ -441,6 +277,211 @@ def periodic_from_mask( # noqa: C901
return pbc_mesh
+ @classmethod
+ def create_empty(cls) -> Self:
+ """Create an empty mesh. Use if you know what you're doing !"""
+ return cls._create()
+
+ @classmethod
+ def copy_mesh(cls, other_mesh: Self) -> Self:
+ """Copy all parameters from another mesh in a new mesh."""
+ mesh = cls._create()
+ mesh.vertices = other_mesh.vertices.copy()
+ mesh.edges = other_mesh.edges.copy()
+ mesh.faces = other_mesh.faces.copy()
+ mesh.width = other_mesh.width
+ mesh.height = other_mesh.height
+ mesh.vertices_params = other_mesh.vertices_params.copy()
+ mesh.edges_params = other_mesh.edges_params.copy()
+ mesh.faces_params = other_mesh.faces_params.copy()
+ mesh.MAX_EDGES_IN_ANY_FACE = other_mesh.MAX_EDGES_IN_ANY_FACE
+
+ return mesh
+
+ def save_mesh(self, path: str) -> None:
+ """Save mesh to a file.
+
+ All PBCMesh data is saved.
+
+ Args:
+ path (str): Path to the saved file. The extension is .npz, a numpy format.
+ """
+ Path(path).parent.mkdir(parents=True, exist_ok=True)
+ np.savez_compressed(
+ path,
+ allow_pickle=False,
+ vertices=self.vertices,
+ edges=self.edges,
+ faces=self.faces,
+ width=self.width,
+ height=self.height,
+ vertices_params=self.vertices_params,
+ edges_params=self.edges_params,
+ faces_params=self.faces_params,
+ MAX_EDGES_IN_ANY_FACE=self.MAX_EDGES_IN_ANY_FACE,
+ )
+
+ @classmethod
+ def load_mesh(cls, path: str) -> Self:
+ """Load a mesh from a file.
+
+ All PBCMesh data is reloaded.
+
+ Args:
+ path (str): Path to the mesh file (.npz), numpy format.
+
+ Returns:
+ Mesh: the mesh loaded from the numpy .npz file.
+ """
+ mesh_file = np.load(path)
+ mesh = cls._create()
+ mesh.vertices, mesh.edges, mesh.faces = (
+ jnp.array(mesh_file["vertices"]),
+ jnp.array(mesh_file["edges"].reshape(-1, 8)),
+ jnp.array(mesh_file["faces"]),
+ )
+ mesh.width, mesh.height = float(mesh_file["width"]), float(mesh_file["height"])
+ mesh.vertices_params = jnp.array(mesh_file["vertices_params"])
+ mesh.edges_params = jnp.array(mesh_file["edges_params"])
+ mesh.faces_params = jnp.array(mesh_file["faces_params"])
+ mesh.MAX_EDGES_IN_ANY_FACE = mesh_file["MAX_EDGES_IN_ANY_FACE"]
+ return mesh
+
+ def save_mesh_txt(
+ self,
+ directory: str,
+ vertices_filename: str = "vertTable.txt",
+ edges_filename: str = "heTable.txt",
+ faces_filename: str = "faceTable.txt",
+ vertices_params_filename: str = "vertParamsTable.txt",
+ edges_params_filename: str = "heParamsTable.txt",
+ faces_params_filename: str = "faceParamsTable.txt",
+ constants_filename: str = "constants.txt",
+ ) -> None:
+ """Save a mesh in separate text files that can be read by numpy.
+
+ Only save the vertices, edges and faces, not other parameters.
+
+ Args:
+ directory (str): Path to the directory where to save the files.
+ vertices_filename (str, optional): Filename for the vertices table. Defaults to "vertTable.txt".
+ edges_filename (str, optional): Filename for the half-edges table. Defaults to "heTable.txt".
+ faces_filename (str, optional): Filename for the faces table. Defaults to "faceTable.txt".
+ vertices_params_filename (str, optional): Filename for the vertices parameters table.
+ Defaults to "vertParamsTable.txt".
+ edges_params_filename (str, optional): Filename for the half-edges parameters table.
+ Defaults to "heParamsTable.txt".
+ faces_params_filename (str, optional): Filename for the faces parameters table.
+ Defaults to "faceParamsTable.txt".
+ constants_filename (str, optional): Filename for width/height/MAX_EDGES_IN_ANY_FACE.
+ Defaults to "constants.txt".
+ """
+ dirpath = Path(directory)
+ dirpath.mkdir(parents=True, exist_ok=True)
+ np.savetxt(dirpath / vertices_filename, self.vertices)
+ np.savetxt(dirpath / edges_filename, self.edges)
+ np.savetxt(dirpath / faces_filename, self.faces)
+ np.savetxt(dirpath / vertices_params_filename, self.vertices_params)
+ np.savetxt(dirpath / edges_params_filename, self.edges_params)
+ np.savetxt(dirpath / faces_params_filename, self.faces_params)
+ with (dirpath / constants_filename).open("w") as f:
+ f.write(f"{self.width} {self.height} {self.MAX_EDGES_IN_ANY_FACE}")
+
+ @classmethod
+ def load_mesh_txt(
+ cls,
+ directory: str,
+ vertices_filename: str = "vertTable.txt",
+ edges_filename: str = "heTable.txt",
+ faces_filename: str = "faceTable.txt",
+ vertices_params_filename: str = "vertParamsTable.txt",
+ edges_params_filename: str = "heParamsTable.txt",
+ faces_params_filename: str = "faceParamsTable.txt",
+ constants_filename: str = "constants.txt",
+ ) -> Self:
+ """Load a mesh from text files.
+
+ Only load the vertices, edges and faces, not other parameters.
+
+ Args:
+ directory (str): Directory where the text files are stored.
+ vertices_filename (str, optional): Filename for the vertices table. Defaults to "vertTable.txt".
+ edges_filename (str, optional): Filename for the half-edges table. Defaults to "heTable.txt".
+ faces_filename (str, optional): Filename for the faces table. Defaults to "faceTable.txt".
+ vertices_params_filename (str, optional): Filename for the vertices parameters table.
+ Defaults to "vertParamsTable.txt".
+ edges_params_filename (str, optional): Filename for the half-edges parameters table.
+ Defaults to "heParamsTable.txt".
+ faces_params_filename (str, optional): Filename for the faces parameters table.
+ Defaults to "faceParamsTable.txt".
+ constants_filename (str, optional): Filename for width/height/MAX_EDGES_IN_ANY_FACE.
+ Defaults to "constants.txt".
+
+ Returns:
+ Self: The loaded mesh.
+ """
+ dirpath = Path(directory)
+ dirpath.mkdir(parents=True, exist_ok=True)
+
+ mesh = cls._create()
+ mesh.vertices = jnp.array(np.loadtxt(dirpath / vertices_filename, dtype=np.float64))
+ mesh.edges = jnp.array(np.loadtxt(dirpath / edges_filename, dtype=np.int64))
+ mesh.faces = jnp.array(np.loadtxt(dirpath / faces_filename, dtype=np.int64))
+ mesh.vertices_params = jnp.array(np.loadtxt(dirpath / vertices_params_filename, dtype=np.float64))
+ mesh.edges_params = jnp.array(np.loadtxt(dirpath / edges_params_filename, dtype=np.int64))
+ mesh.faces_params = jnp.array(np.loadtxt(dirpath / faces_params_filename, dtype=np.int64))
+ with (dirpath / constants_filename).open("r") as f:
+ numbers = f.readline().split()
+ mesh.width = float(numbers[0])
+ mesh.height = float(numbers[1])
+ mesh.MAX_EDGES_IN_ANY_FACE = int(numbers[2])
+ return mesh
+
+ def get_length(self, half_edge_id: Array) -> Array:
+ """Get the length of given edges."""
+
+ def _get_length(half_edge_id: Array) -> Array:
+ return get_length(half_edge_id, self.vertices, self.edges, self.faces, self.width, self.height)
+
+ return jax.vmap(_get_length)(half_edge_id)
+
+ def get_length_with_offset(self, half_edge_id: Array) -> Array:
+ """Get the length of given edges along with its offsets in an array (length, offset x, offset y)."""
+
+ def _get_length_with_offset(half_edge_id: Array) -> Array:
+ return get_length_with_offset(half_edge_id, self.vertices, self.edges, self.faces, self.width, self.height)
+
+ return jax.vmap(_get_length_with_offset)(half_edge_id)
+
+ def get_perimeter(self, face_id: Array) -> Array:
+ """Get the perimeter of given faces."""
+
+ def _get_perimeter(face_id: Array) -> Array:
+ return get_perimeter(
+ face_id, self.vertices, self.edges, self.faces, self.width, self.height, self.MAX_EDGES_IN_ANY_FACE
+ )
+
+ return jax.vmap(_get_perimeter)(face_id)
+
+ def get_area(self, face_id: Array) -> Array:
+ """Get the area of given faces."""
+
+ def _get_area(face_id: Array) -> Array:
+ return get_area(
+ face_id, self.vertices, self.edges, self.faces, self.width, self.height, self.MAX_EDGES_IN_ANY_FACE
+ )
+
+ return jax.vmap(_get_area)(face_id)
+
+ def update_boundary_conditions(self) -> None:
+ """Force periodic boundary conditions again after an update.
+
+ Most likely you'll never have to use it yourself, as it can be made automatically with a `PbcBilevelOptimizer`.
+ """
+ self.vertices, self.edges, self.faces = update_pbc(
+ self.vertices, self.edges, self.faces, self.width, self.height
+ )
+
def _make_periodic( # noqa: C901
seeds: Array,
diff --git a/src/vertax/meshes/plot.py b/src/vertax/meshes/plot.py
index f73413e..5d69daa 100644
--- a/src/vertax/meshes/plot.py
+++ b/src/vertax/meshes/plot.py
@@ -25,28 +25,41 @@ class FacePlot(Enum):
"""What it is possible to show on a face."""
MULTICOLOR = 1
+ """Each face get a random color."""
FACE_PARAMETER = 2
+ """The face color depends on its parameter."""
AREA = 3
+ """The face color depends on its area."""
PERIMETER = 4
+ """The face color depends on its perimeter."""
WHITE = 5
+ """All faces are just white."""
FATES = 6
+ """Faces are colored depending on their fate marker (if any)."""
class EdgePlot(Enum):
"""What it is possible to show on an edge."""
BLACK = 1
+ """All edges are black."""
EDGE_PARAMETER = 2
+ """The edge color depends on its parameter."""
LENGTH = 3
+ """The edge color depends on its length."""
INVISIBLE = 4
+ """Do not show edges."""
class VertexPlot(Enum):
"""What it is possible to show on a vertex."""
BLACK = 1
+ """Vertices are black."""
VERTEX_PARAMETER = 2
+ """The vertex color depends on its parameter."""
INVISIBLE = 3
+ """Do not show vertices."""
def add_colorbar(fig: Figure, ax: Axes, v_min: float, v_max: float, cmap: Colormap) -> Colorbar:
@@ -108,6 +121,9 @@ def plot_mesh(
vertices_cmap_name: str = "spring",
vertices_size: float = 20,
title: str = "",
+ forced_vertex_scale: tuple[float, float] | None = None,
+ forced_edge_scale: tuple[float, float] | None = None,
+ forced_face_scale: tuple[float, float] | None = None,
) -> None:
"""Plot the mesh and decide to save and/or show the mesh or not."""
if isinstance(mesh, PbcMesh):
@@ -128,6 +144,9 @@ def plot_mesh(
vertices_cmap_name,
vertices_size,
title,
+ forced_vertex_scale,
+ forced_edge_scale,
+ forced_face_scale,
)
elif isinstance(mesh, BoundedMesh):
_plot_bounded_mesh(
@@ -147,6 +166,9 @@ def plot_mesh(
vertices_cmap_name,
vertices_size,
title,
+ forced_vertex_scale,
+ forced_edge_scale,
+ forced_face_scale,
)
@@ -164,6 +186,9 @@ def get_plot_mesh(
vertices_cmap_name: str = "spring",
vertices_size: float = 20,
title: str = "",
+ forced_vertex_scale: tuple[float, float] | None = None,
+ forced_edge_scale: tuple[float, float] | None = None,
+ forced_face_scale: tuple[float, float] | None = None,
) -> tuple[Figure, Axes]:
"""Get the matplotlib figure and and ax for one plot."""
if isinstance(mesh, PbcMesh):
@@ -181,6 +206,9 @@ def get_plot_mesh(
vertices_cmap_name,
vertices_size,
title,
+ forced_vertex_scale,
+ forced_edge_scale,
+ forced_face_scale,
)
elif isinstance(mesh, BoundedMesh):
return _get_plot_bounded_mesh(
@@ -197,6 +225,9 @@ def get_plot_mesh(
vertices_cmap_name,
vertices_size,
title,
+ forced_vertex_scale,
+ forced_edge_scale,
+ forced_face_scale,
)
else:
msg = f"Expected either a PbcMesh or a BoundedMesh. Got {mesh} instead."
@@ -220,6 +251,9 @@ def _plot_pbc_mesh(
vertices_cmap_name: str = "spring",
vertices_size: float = 20,
title: str = "",
+ forced_vertex_scale: tuple[float, float] | None = None,
+ forced_edge_scale: tuple[float, float] | None = None,
+ forced_face_scale: tuple[float, float] | None = None,
) -> None:
"""Plot the mesh and decide to save and/or show the mesh or not."""
fig, _ax = _get_plot_pbc_mesh(
@@ -236,6 +270,9 @@ def _plot_pbc_mesh(
vertices_cmap_name,
vertices_size,
title,
+ forced_vertex_scale,
+ forced_edge_scale,
+ forced_face_scale,
)
if save:
@@ -261,6 +298,9 @@ def _get_plot_pbc_mesh(
vertices_cmap_name: str = "spring",
vertices_size: float = 20,
title: str = "",
+ forced_vertex_scale: tuple[float, float] | None = None,
+ forced_edge_scale: tuple[float, float] | None = None,
+ forced_face_scale: tuple[float, float] | None = None,
) -> tuple[Figure, Axes]:
"""Get the matplotlib figure and and ax for one plot."""
# Fates not used for pbc.
@@ -269,9 +309,11 @@ def _get_plot_pbc_mesh(
fig, _ = plt.subplots(layout="constrained")
ax = plt.gca()
- _plot_faces_pbc(mesh, fig, ax, face_plot, faces_cmap_name, face_parameters_name)
- _plot_edges_pbc(mesh, fig, ax, edge_plot, edges_cmap_name, edges_width, edge_parameters_name)
- _plot_vertices_pbc(mesh, fig, ax, vertex_plot, vertices_cmap_name, vertices_size, vertex_parameters_name)
+ _plot_faces_pbc(mesh, fig, ax, face_plot, faces_cmap_name, face_parameters_name, forced_face_scale)
+ _plot_edges_pbc(mesh, fig, ax, edge_plot, edges_cmap_name, edges_width, edge_parameters_name, forced_edge_scale)
+ _plot_vertices_pbc(
+ mesh, fig, ax, vertex_plot, vertices_cmap_name, vertices_size, vertex_parameters_name, forced_vertex_scale
+ )
ax.set_title(title)
ax.set_aspect(mesh.height / mesh.width)
@@ -282,7 +324,13 @@ def _get_plot_pbc_mesh(
def _plot_faces_pbc(
- mesh: PbcMesh, fig: Figure, ax: Axes, face_plot: FacePlot, faces_cmap_name: str, face_parameters_name: str
+ mesh: PbcMesh,
+ fig: Figure,
+ ax: Axes,
+ face_plot: FacePlot,
+ faces_cmap_name: str,
+ face_parameters_name: str,
+ forced_scale: tuple[float, float] | None = None,
) -> None:
multicolor_cmap = _get_multicolor_face_cmap(mesh)
faces_cmap = matplotlib.colormaps.get_cmap(faces_cmap_name)
@@ -302,8 +350,11 @@ def _plot_faces_pbc(
case FacePlot.PERIMETER:
values = mesh.get_perimeter(jnp.arange(mesh.nb_faces))
cbar_label = "Perimeter of cell"
- v_max = float(values.max())
- v_min = float(values.min())
+ if forced_scale is None:
+ v_max = float(values.max())
+ v_min = float(values.min())
+ else:
+ v_min, v_max = forced_scale
match face_plot:
case FacePlot.MULTICOLOR | FacePlot.WHITE:
pass
@@ -408,6 +459,7 @@ def _plot_edges_pbc(
edges_cmap_name: str,
edges_width: float,
edge_parameters_name: str,
+ forced_scale: tuple[float, float] | None = None,
) -> None:
if edge_plot != EdgePlot.INVISIBLE:
edge_params_cmap = matplotlib.colormaps.get_cmap(edges_cmap_name)
@@ -425,8 +477,11 @@ def _plot_edges_pbc(
case EdgePlot.LENGTH:
values = mesh.get_length(jnp.arange(2 * mesh.nb_edges))
cbar_label = "Length of edge"
- v_max = float(values.max())
- v_min = float(values.min())
+ if forced_scale is None:
+ v_max = float(values.max())
+ v_min = float(values.min())
+ else:
+ v_min, v_max = forced_scale
match edge_plot:
case EdgePlot.BLACK:
@@ -478,6 +533,7 @@ def _plot_vertices_pbc(
vertices_cmap_name: str,
vertices_size: float,
vertex_parameters_name: str,
+ forced_scale: tuple[float, float] | None = None,
) -> None:
if vertex_plot != VertexPlot.INVISIBLE:
# set the correct colorbar
@@ -485,8 +541,11 @@ def _plot_vertices_pbc(
v_max = 1
v_min = 0
if vertex_plot == VertexPlot.VERTEX_PARAMETER:
- v_max = float(mesh.vertices_params.max())
- v_min = float(mesh.vertices_params.min())
+ if forced_scale is None:
+ v_max = float(mesh.vertices_params.max())
+ v_min = float(mesh.vertices_params.min())
+ else:
+ v_min, v_max = forced_scale
cbar = add_colorbar(fig, ax, v_min, v_max, vertices_params_cmap)
cbar_label = "Vertex parameter" if vertex_parameters_name == "" else vertex_parameters_name
cbar.ax.set_ylabel(cbar_label, rotation=270, labelpad=13)
@@ -523,6 +582,9 @@ def _plot_bounded_mesh(
vertices_cmap_name: str = "spring",
vertices_size: float = 20,
title: str = "",
+ forced_vertex_scale: tuple[float, float] | None = None,
+ forced_edge_scale: tuple[float, float] | None = None,
+ forced_face_scale: tuple[float, float] | None = None,
) -> None:
"""Plot the mesh and decide to save and/or show the mesh or not."""
fig, _ax = _get_plot_bounded_mesh(
@@ -539,6 +601,9 @@ def _plot_bounded_mesh(
vertices_cmap_name,
vertices_size,
title,
+ forced_vertex_scale,
+ forced_edge_scale,
+ forced_face_scale,
)
if save:
@@ -564,13 +629,18 @@ def _get_plot_bounded_mesh(
vertices_cmap_name: str = "spring",
vertices_size: float = 20,
title: str = "",
+ forced_vertex_scale: tuple[float, float] | None = None,
+ forced_edge_scale: tuple[float, float] | None = None,
+ forced_face_scale: tuple[float, float] | None = None,
) -> tuple[Figure, Axes]:
"""Get the matplotlib figure and and ax for one plot."""
fig, _ = plt.subplots(layout="constrained")
ax = plt.gca()
- _plot_faces_bounded(mesh, fig, ax, face_plot, faces_cmap_name, face_parameters_name)
- _plot_edges_bounded(mesh, fig, ax, edge_plot, edges_cmap_name, edges_width, edge_parameters_name)
- _plot_vertices_bounded(mesh, fig, ax, vertex_plot, vertices_cmap_name, vertices_size, vertex_parameters_name)
+ _plot_faces_bounded(mesh, fig, ax, face_plot, faces_cmap_name, face_parameters_name, forced_face_scale)
+ _plot_edges_bounded(mesh, fig, ax, edge_plot, edges_cmap_name, edges_width, edge_parameters_name, forced_edge_scale)
+ _plot_vertices_bounded(
+ mesh, fig, ax, vertex_plot, vertices_cmap_name, vertices_size, vertex_parameters_name, forced_vertex_scale
+ )
ax.set_title(title)
# unlike the pbc case, here is not easy to know a priori
@@ -583,7 +653,13 @@ def _get_plot_bounded_mesh(
def _plot_faces_bounded(
- mesh: BoundedMesh, fig: Figure, ax: Axes, face_plot: FacePlot, faces_cmap_name: str, face_parameters_name: str
+ mesh: BoundedMesh,
+ fig: Figure,
+ ax: Axes,
+ face_plot: FacePlot,
+ faces_cmap_name: str,
+ face_parameters_name: str,
+ forced_scale: tuple[float, float] | None = None,
) -> None:
multicolor_cmap = _get_multicolor_face_cmap(mesh)
faces_cmap = matplotlib.colormaps.get_cmap(faces_cmap_name)
@@ -605,8 +681,11 @@ def _plot_faces_bounded(
cbar_label = "Perimeter of cell"
case FacePlot.FATES:
values = mesh.faces[:, 1]
- v_max = float(values.max())
- v_min = float(values.min())
+ if forced_scale is None:
+ v_max = float(values.max())
+ v_min = float(values.min())
+ else:
+ v_min, v_max = forced_scale
match face_plot:
case FacePlot.MULTICOLOR | FacePlot.WHITE | FacePlot.FATES:
pass
@@ -681,6 +760,7 @@ def _plot_edges_bounded(
edges_cmap_name: str,
edges_width: float,
edge_parameters_name: str,
+ forced_scale: tuple[float, float] | None = None,
) -> None:
if edge_plot != EdgePlot.INVISIBLE:
edge_params_cmap = matplotlib.colormaps.get_cmap(edges_cmap_name)
@@ -697,8 +777,11 @@ def _plot_edges_bounded(
case EdgePlot.LENGTH:
values = mesh.get_length(jnp.arange(2 * mesh.nb_edges))
cbar_label = "Length of edge"
- v_max = float(values.max())
- v_min = float(values.min())
+ if forced_scale is None:
+ v_max = float(values.max())
+ v_min = float(values.min())
+ else:
+ v_min, v_max = forced_scale
# set the correct colorbar if needed
match edge_plot:
@@ -756,6 +839,7 @@ def _plot_vertices_bounded(
vertices_cmap_name: str,
vertices_size: float,
vertex_parameters_name: str,
+ forced_scale: tuple[float, float] | None = None,
) -> None:
if vertex_plot != VertexPlot.INVISIBLE:
# set the correct colorbar
@@ -763,8 +847,11 @@ def _plot_vertices_bounded(
v_max = 1
v_min = 0
if vertex_plot == VertexPlot.VERTEX_PARAMETER:
- v_max = float(mesh.vertices_params.max())
- v_min = float(mesh.vertices_params.min())
+ if forced_scale is None:
+ v_max = float(mesh.vertices_params.max())
+ v_min = float(mesh.vertices_params.min())
+ else:
+ v_min, v_max = forced_scale
cbar = add_colorbar(fig, ax, v_min, v_max, vertices_params_cmap)
cbar_label = "Vertex parameter" if vertex_parameters_name == "" else vertex_parameters_name
cbar.ax.set_ylabel(cbar_label, rotation=270, labelpad=13)
diff --git a/src/vertax/method_enum.py b/src/vertax/method_enum.py
index 2df67c5..8753077 100644
--- a/src/vertax/method_enum.py
+++ b/src/vertax/method_enum.py
@@ -7,6 +7,14 @@ class BilevelOptimizationMethod(Enum):
"""Which optimization method to use in the bi-level optimization."""
AUTOMATIC_DIFFERENTIATION = "ad"
+ """Unrolls the inner optimization steps ; forward-mode JVP via `jax.jacfwd`,
+ cost scales with the number of parameters and iterations."""
EQUILIBRIUM_PROPAGATION = "ep"
+ """Estimates the gradient from perturbed free vs nudged equilibria ; no backdrop required.
+ Most efficient but depends on the perturbation size β."""
IMPLICIT_DIFFERENTIATION = "id"
+ """Differentiates the optimality condition ∇ₓE=0 via Implicit Function Theorem ; JVP variant ;
+ requires Hessian solve and sensitive to ill-conditioning."""
ADJOINT_STATE = "as"
+ """Differentiates the optimality condition ∇ₓE=0 via Implicit Function Theorem ; VJP variant ;
+ requires Hessian solve or sensitive to ill-conditioning."""
diff --git a/src/vertax/opt.py b/src/vertax/opt.py
index 49b10a0..d28fbd6 100644
--- a/src/vertax/opt.py
+++ b/src/vertax/opt.py
@@ -8,7 +8,7 @@
import jax.numpy as jnp
import numpy as np
import optax
-from jax import Array, grad, jacfwd, jit, jvp, lax
+from jax import Array, grad, jacfwd, jit, jvp, lax, value_and_grad
from numpy.typing import ArrayLike
from scipy.sparse.linalg import LinearOperator, minres
@@ -143,7 +143,7 @@ def _jit_minimize(
step_count = jnp.array(0)
should_stop = jnp.array(False)
- # 2. Define the inner step function for lax.scan
+ # Define the inner step function for lax.scan
# The inner function is defined *without* @jit because it's compiled by lax.scan/jit on minimize
def scan_step(
carry: tuple[Array, Array, Array, Array, Array, Array, Array, Array, Array, int, Array, Array], i: int
@@ -166,7 +166,7 @@ def scan_step(
# Determine if we are still running (i.e., not stopped)
is_running = jnp.logical_not(should_stop)
- # 1) Compute loss
+ # Compute loss
L_current = L_in(
vt[selected_verts],
ht[selected_hes],
@@ -176,23 +176,23 @@ def scan_step(
fp[selected_faces],
)
- # 2) Early stopping bookkeeping
+ # Early stopping bookkeeping
denom = jnp.where(prev_L_values[-1] != 0, prev_L_values[-1], 1.0)
rel_var = jnp.abs((L_current - prev_L_values[-1]) / denom)
new_stagnation_count = jnp.where(rel_var < tolerance, stagnation_count + 1, 0)
new_should_stop = (new_stagnation_count >= patience) | (i >= iterations_max - 1)
- # 3) Gradient wrt chosen argnums
+ # Gradient wrt chosen argnums
grads = grad(L_in, argnums=optimization_target.value)(vt, ht, ft, vp, hp, fp)
- # 4) Optimizer update
+ # Optimizer update
updates, new_opt_state = solver.update(grads, opt_state)
- # 5) Apply updates to the chosen array on selected indices
+ # Apply updates to the chosen array on selected indices
updates_sel = updates.at[sel].get() # type: ignore
- # --- Conditional Application of Optimization Update and State ---
+ # Conditional application of optimization update and state
arrays = [vt, ht, ft, vp, hp, fp]
k = optimization_target.value
new_optim = arrays[k].at[sel].set(arrays[k][sel] + updates_sel)
@@ -206,7 +206,7 @@ def scan_step(
stagnation_count = lax.cond(is_running, lambda: new_stagnation_count, lambda: stagnation_count)
should_stop = new_should_stop # should_stop is a flag for the *next* iteration
- # 6) Geometry updates (Must also be conditional/masked)
+ # Geometry updates (must also be conditional/masked)
new_vt_pbc, new_ht_pbc, new_ft_pbc = update_pbc(vt, ht, ft, width, height)
vt = lax.cond(is_running, lambda: new_vt_pbc, lambda: vt)
ht = lax.cond(is_running, lambda: new_ht_pbc, lambda: ht)
@@ -219,12 +219,12 @@ def scan_step(
ht = lax.cond(is_running, lambda: new_ht_T1, lambda: ht)
ft = lax.cond(is_running, lambda: new_ft_T1, lambda: ft)
- # 7) Shift prev_L_values
+ # Shift prev_L_values
new_prev_L_values = prev_L_values.at[1:].set(prev_L_values[:-1])
new_prev_L_values = new_prev_L_values.at[0].set(L_current)
prev_L_values = lax.cond(is_running, lambda: new_prev_L_values, lambda: prev_L_values)
- # 8) Update History and Step Count
+ # Update history and step count
L_list = L_list.at[i].set(L_current)
step_count = i + 1
@@ -244,8 +244,7 @@ def scan_step(
)
return new_carry, None
- # 3. Call lax.scan
-
+ # Call lax.scan
init_carry = (
vertTable,
heTable,
@@ -263,7 +262,7 @@ def scan_step(
final_state, _ = lax.scan(scan_step, init_carry, xs=jnp.arange(iterations_max)) # ty:ignore[invalid-argument-type]
- # 4. Unpack and return results (for slicing outside JIT)
+ # Unpack and return results (for slicing outside jit)
vt_f, ht_f, ft_f, vp_f, hp_f, fp_f, _, _, _, step_f, _, L_hist = final_state
return (vt_f, ht_f, ft_f, vp_f, hp_f, fp_f), (L_hist, step_f) # ty:ignore[invalid-return-type]
@@ -377,13 +376,140 @@ def inner_opt(
# Convert the JAX array step_f to a Python integer
step_f = step_f_array.item()
- # Now slice using standard Python/NumPy slicing
+ # Slice using standard Python/NumPy slicing
final_L_list = L_hist_full[:step_f]
# Return updated arrays and loss history
return (vt_f, ht_f, ft_f), final_L_list
+# ----------------------- #
+# Sinkhorn implementation #
+# ----------------------- #
+
+# def _periodic_sq_cost_matrix(
+# vertTable_after: Array,
+# vertTable_target: Array,
+# width: float,
+# height: float,
+# ) -> Array:
+# """Pairwise periodic squared distance, shape (n, n)."""
+# sim = vertTable_after[:, :2]
+# tgt = vertTable_target[:, :2]
+# diff = sim[:, None, :] - tgt[None, :, :] # (n, n, 2)
+# shifts = jnp.array(
+# [
+# [0.0, 0.0],
+# [-width, 0.0], [width, 0.0],
+# [0.0, -height], [0.0, height],
+# [-width, -height], [-width, height],
+# [width, -height], [width, height],
+# ]
+# ) # (9, 2)
+# shifted = diff[:, :, None, :] - shifts[None, None, :, :] # (n, n, 9, 2)
+# sq = jnp.sum(shifted * shifted, axis=-1) # (n, n, 9)
+# return jnp.min(sq, axis=-1) # (n, n)
+#
+# from ott.geometry import geometry
+# from ott.problems.linear import linear_problem
+# from ott.solvers.linear import sinkhorn
+# def _build_t1_repair_perm_sinkhorn(
+# vertTable_after: Array,
+# vertTable_target: Array,
+# width: float,
+# height: float,
+# epsilon: float = 1e-3,
+# threshold: float = 1e-3,
+# max_iterations: int = 1000,
+# ) -> Array:
+# """Vertex relabeling by entropic OT (ott-jax Sinkhorn)."""
+# C = _periodic_sq_cost_matrix(vertTable_after, vertTable_target, width, height)
+# geom = geometry.Geometry(cost_matrix=C, epsilon=epsilon)
+# prob = linear_problem.LinearProblem(geom)
+# solver = sinkhorn.Sinkhorn(threshold=threshold, max_iterations=max_iterations)
+# out = solver(prob)
+# return jnp.argmax(out.matrix, axis=1).astype(jnp.int32)
+
+
+def _periodic_sq_dist(p: Array, q: Array, width: float, height: float) -> Array:
+ """Squared distance under PBC between two 2D points (cols 0,1).
+
+ Mirrors `cost_v2v`'s convention: take the minimum over the 9 image shifts
+ (center + 8 surrounding sectors) of the target.
+ """
+ dx = p[0] - q[0]
+ dy = p[1] - q[1]
+ candidates = jnp.array(
+ [
+ dx * dx + dy * dy,
+ (dx - width) * (dx - width) + dy * dy,
+ (dx + width) * (dx + width) + dy * dy,
+ dx * dx + (dy - height) * (dy - height),
+ dx * dx + (dy + height) * (dy + height),
+ (dx - width) * (dx - width) + (dy - height) * (dy - height),
+ (dx - width) * (dx - width) + (dy + height) * (dy + height),
+ (dx + width) * (dx + width) + (dy - height) * (dy - height),
+ (dx + width) * (dx + width) + (dy + height) * (dy + height),
+ ]
+ )
+ return jnp.min(candidates)
+
+
+def _build_t1_repair_perm(
+ vertTable_after: Array,
+ vertTable_target: Array,
+ heTable_before: Array,
+ heTable_after: Array,
+ width: float,
+ height: float,
+) -> Array:
+ """Build a vertex-index permutation that swaps T1-flipped pairs when it lowers v2v cost.
+
+ A half-edge whose face index (col 5) changed across `inner_opt` was part of a T1: only
+ `update_T1` ever writes that column (`update_pbc` does not). For each such half-edge,
+ cols (3, 4) give the (a, b) vertex pair to test. Each T1 is reported twice (edge + twin);
+ the swap-only-if-better filter is idempotent, so duplicates are harmless.
+ """
+ perm = jnp.arange(vertTable_after.shape[0], dtype=jnp.int32)
+
+ t1_mask = heTable_before[:, 5] != heTable_after[:, 5]
+ pair_a = heTable_before[:, 3].astype(jnp.int32)
+ pair_b = heTable_before[:, 4].astype(jnp.int32)
+
+ def body(i: int, perm: Array) -> Array:
+ a = pair_a[i]
+ b = pair_b[i]
+ ta = vertTable_target[perm[a]]
+ tb = vertTable_target[perm[b]]
+ pa = vertTable_after[a]
+ pb = vertTable_after[b]
+ cost_no_swap = _periodic_sq_dist(pa, ta, width, height) + _periodic_sq_dist(pb, tb, width, height)
+ cost_swap = _periodic_sq_dist(pa, tb, width, height) + _periodic_sq_dist(pb, ta, width, height)
+ do_swap = t1_mask[i] & (cost_swap < cost_no_swap)
+ new_a_val = jnp.where(do_swap, perm[b], perm[a])
+ new_b_val = jnp.where(do_swap, perm[a], perm[b])
+ return perm.at[a].set(new_a_val).at[b].set(new_b_val)
+
+ return lax.fori_loop(0, heTable_before.shape[0], body, perm)
+
+
+def _apply_perm_to_state(
+ perm: Array,
+ vertTable: Array,
+ heTable: Array,
+) -> tuple[Array, Array]:
+ """Reorder vertTable rows and relabel vertex indices stored in heTable cols 3, 4.
+
+ heTable cols 0, 1, 2, 5 reference half-edges/faces and are unaffected.
+ Self-canceling for any cost that reads positions by half-edge (only cost_v2v sees the swap).
+ """
+ new_vertTable = vertTable[perm]
+ src = perm[heTable[:, 3].astype(jnp.int32)].astype(heTable.dtype)
+ tgt = perm[heTable[:, 4].astype(jnp.int32)].astype(heTable.dtype)
+ new_heTable = heTable.at[:, 3].set(src).at[:, 4].set(tgt)
+ return new_vertTable, new_heTable
+
+
def cost_ad(
vertTable: Array,
heTable: Array,
@@ -408,8 +534,10 @@ def cost_ad(
selected_faces: Array | None = None,
image_target: Array | None = None,
update_t1_func: UpdateT1Func = update_T1,
-) -> Array:
+) -> tuple[Array, tuple[Array, Array, Array]]:
"""Automatic differentiation cost function."""
+ heTable_before = heTable
+
(vertTable, heTable, faceTable), _loss = inner_opt(
vertTable,
heTable,
@@ -431,6 +559,16 @@ def cost_ad(
update_t1_func,
)
+ perm = _build_t1_repair_perm(
+ vertTable,
+ vertTable_target,
+ heTable_before,
+ heTable,
+ width,
+ height,
+ )
+ vertTable, heTable = _apply_perm_to_state(perm, vertTable, heTable)
+
loss_out_value = L_out(
vertTable,
heTable,
@@ -446,7 +584,7 @@ def cost_ad(
image_target,
)
- return loss_out_value
+ return loss_out_value, (vertTable, heTable, faceTable)
def outer_opt(
@@ -474,61 +612,11 @@ def outer_opt(
selected_faces: Array | None,
image_target: Array | None,
update_t1_func: UpdateT1Func = update_T1,
-) -> tuple[Array, Array, Array]:
+) -> tuple[Array, Array, Array, Array, Array, Array]:
"""Outer optimization for Automatic differentiation method."""
- grad_verts = grad(cost_ad, argnums=5)(
- vertTable,
- heTable,
- faceTable,
- width,
- height,
- vert_params,
- he_params,
- face_params,
- vertTable_target,
- heTable_target,
- faceTable_target,
- L_in,
- L_out,
- solver_inner,
- min_dist_T1,
- iterations_max,
- tolerance,
- patience,
- selected_verts,
- selected_hes,
- selected_faces,
- image_target,
- update_t1_func,
- )
-
- grad_hes = grad(cost_ad, argnums=6)(
- vertTable,
- heTable,
- faceTable,
- width,
- height,
- vert_params,
- he_params,
- face_params,
- vertTable_target,
- heTable_target,
- faceTable_target,
- L_in,
- L_out,
- solver_inner,
- min_dist_T1,
- iterations_max,
- tolerance,
- patience,
- selected_verts,
- selected_hes,
- selected_faces,
- image_target,
- update_t1_func,
- )
-
- grad_faces = grad(cost_ad, argnums=7)(
+ ((_, (vertTable, heTable, faceTable)), (grad_verts, grad_hes, grad_faces)) = value_and_grad(
+ cost_ad, argnums=(5, 6, 7), has_aux=True
+ )(
vertTable,
heTable,
faceTable,
@@ -563,7 +651,7 @@ def outer_opt(
new_he_params: Array = updated_params["he_params"] # type: ignore
new_face_params: Array = updated_params["face_params"] # type: ignore
- return new_vert_params, new_he_params, new_face_params
+ return vertTable, heTable, faceTable, new_vert_params, new_he_params, new_face_params
#############################
@@ -793,8 +881,10 @@ def outer_eq_prop(
image_target: Array | None,
beta: float,
update_t1_func: UpdateT1Func = update_T1,
-) -> tuple[Array, Array, Array]:
+) -> tuple[Array, Array, Array, Array, Array, Array]:
"""Outer optimization for equilibrium propagation method."""
+ heTable_before = heTable
+
(vertTable_free, heTable_free, faceTable_free), _ = inner_eq_prop(
vertTable,
heTable,
@@ -823,6 +913,16 @@ def outer_eq_prop(
update_t1_func,
)
+ perm_free = _build_t1_repair_perm(
+ vertTable_free,
+ vertTable_target,
+ heTable_before,
+ heTable_free,
+ width,
+ height,
+ )
+ vertTable_free, heTable_free = _apply_perm_to_state(perm_free, vertTable_free, heTable_free)
+
(vertTable_nudged, heTable_nudged, faceTable_nudged), _ = inner_eq_prop(
vertTable,
heTable,
@@ -851,91 +951,19 @@ def outer_eq_prop(
update_t1_func,
)
- grad_loss_ep_free_verts = grad(_loss_ep_static, argnums=5)(
- vertTable_free,
- heTable_free,
- faceTable_free,
- width,
- height,
- vert_params,
- he_params,
- face_params,
- vertTable_target,
- heTable_target,
- faceTable_target,
- L_in,
- L_out,
- selected_verts,
- selected_hes,
- selected_faces,
- image_target,
- beta=-beta,
- )
-
- grad_loss_ep_nudged_verts = grad(_loss_ep_static, argnums=5)(
+ perm_nudged = _build_t1_repair_perm(
vertTable_nudged,
- heTable_nudged,
- faceTable_nudged,
- width,
- height,
- vert_params,
- he_params,
- face_params,
vertTable_target,
- heTable_target,
- faceTable_target,
- L_in,
- L_out,
- selected_verts,
- selected_hes,
- selected_faces,
- image_target,
- beta,
- )
-
- grad_loss_ep_free_hes = grad(_loss_ep_static, argnums=6)(
- vertTable_free,
- heTable_free,
- faceTable_free,
- width,
- height,
- vert_params,
- he_params,
- face_params,
- vertTable_target,
- heTable_target,
- faceTable_target,
- L_in,
- L_out,
- selected_verts,
- selected_hes,
- selected_faces,
- image_target,
- beta=-beta,
- )
-
- grad_loss_ep_nudged_hes = grad(_loss_ep_static, argnums=6)(
- vertTable_nudged,
+ heTable_before,
heTable_nudged,
- faceTable_nudged,
width,
height,
- vert_params,
- he_params,
- face_params,
- vertTable_target,
- heTable_target,
- faceTable_target,
- L_in,
- L_out,
- selected_verts,
- selected_hes,
- selected_faces,
- image_target,
- beta,
)
+ vertTable_nudged, heTable_nudged = _apply_perm_to_state(perm_nudged, vertTable_nudged, heTable_nudged)
+
+ grad_ep = grad(_loss_ep_static, argnums=(5, 6, 7))
- grad_loss_ep_free_faces = grad(_loss_ep_static, argnums=7)(
+ grad_loss_ep_free_verts, grad_loss_ep_free_hes, grad_loss_ep_free_faces = grad_ep(
vertTable_free,
heTable_free,
faceTable_free,
@@ -956,7 +984,7 @@ def outer_eq_prop(
beta=-beta,
)
- grad_loss_ep_nudged_faces = grad(_loss_ep_static, argnums=7)(
+ grad_loss_ep_nudged_verts, grad_loss_ep_nudged_hes, grad_loss_ep_nudged_faces = grad_ep(
vertTable_nudged,
heTable_nudged,
faceTable_nudged,
@@ -990,7 +1018,7 @@ def outer_eq_prop(
he_params = updated_params["he_params"] # type: ignore
face_params = updated_params["face_params"] # type: ignore
- return vert_params, he_params, face_params
+ return vertTable_free, heTable_free, faceTable_free, vert_params, he_params, face_params
###########################
@@ -1023,7 +1051,7 @@ def outer_implicit(
selected_faces: Array | None,
image_target: Array | None,
update_t1_func: UpdateT1Func = update_T1,
-) -> tuple[Array, Array, Array]:
+) -> tuple[Array, Array, Array, Array, Array, Array]:
"""Outer optimization for implicit differentiation method."""
def L_in_flatten( # noqa: N802
@@ -1037,6 +1065,8 @@ def L_in_flatten( # noqa: N802
vertTable_tmp = vertTable_flatten.reshape(len(vertTable_flatten) // 2, 2)
return L_in(vertTable_tmp, heTable, faceTable, vert_params, he_params, face_params)
+ heTable_before = heTable
+
(vertTable_eq, heTable_eq, faceTable_eq), _ = inner_opt(
vertTable,
heTable,
@@ -1058,6 +1088,16 @@ def L_in_flatten( # noqa: N802
update_t1_func,
)
+ perm = _build_t1_repair_perm(
+ vertTable_eq,
+ vertTable_target,
+ heTable_before,
+ heTable_eq,
+ width,
+ height,
+ )
+ vertTable_eq, heTable_eq = _apply_perm_to_state(perm, vertTable_eq, heTable_eq)
+
vert_flat_eq = vertTable_eq.flatten()
# H = d^2 L_in / d vertTable^2
@@ -1176,7 +1216,7 @@ def matvec(v: ArrayLike) -> Array:
he_params = updated_params["he_params"] # type: ignore
face_params = updated_params["face_params"] # type: ignore
- return vert_params, he_params, face_params
+ return vertTable, heTable, faceTable, vert_params, he_params, face_params
##########################
@@ -1209,7 +1249,7 @@ def outer_adjoint_state(
selected_faces: Array | None,
image_target: Array | None,
update_t1_func: UpdateT1Func = update_T1,
-) -> tuple[Array, Array, Array]:
+) -> tuple[Array, Array, Array, Array, Array, Array]:
"""Outer optimization for adjoint state method."""
def L_in_flatten( # noqa: N802
@@ -1223,6 +1263,8 @@ def L_in_flatten( # noqa: N802
vertTable_tmp = vertTable_flatten.reshape(len(vertTable_flatten) // 2, 2)
return L_in(vertTable_tmp, heTable, faceTable, vert_params, he_params, face_params)
+ heTable_before = heTable
+
(vertTable_eq, heTable_eq, faceTable_eq), _L_in_value = inner_opt(
vertTable,
heTable,
@@ -1244,6 +1286,16 @@ def L_in_flatten( # noqa: N802
update_t1_func,
)
+ perm = _build_t1_repair_perm(
+ vertTable_eq,
+ vertTable_target,
+ heTable_before,
+ heTable_eq,
+ width,
+ height,
+ )
+ vertTable_eq, heTable_eq = _apply_perm_to_state(perm, vertTable_eq, heTable_eq)
+
vertTable_eq_flat = vertTable_eq.flatten()
G_op = grad(L_in_flatten, argnums=0)
@@ -1310,7 +1362,7 @@ def matvec(v: ArrayLike) -> Array:
he_params = updated_params["he_params"] # type: ignore
face_params = updated_params["face_params"] # type: ignore
- return vert_params, he_params, face_params
+ return vertTable, heTable, faceTable, vert_params, he_params, face_params
#############
@@ -1349,7 +1401,7 @@ def bilevel_opt(
"""Bilevel optimization for PBC meshes."""
match method:
case BilevelOptimizationMethod.AUTOMATIC_DIFFERENTIATION:
- vert_params, he_params, face_params = outer_opt(
+ vertTable, heTable, faceTable, vert_params, he_params, face_params = outer_opt(
vertTable,
heTable,
faceTable,
@@ -1377,7 +1429,7 @@ def bilevel_opt(
)
case BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION:
- vert_params, he_params, face_params = outer_eq_prop(
+ vertTable, heTable, faceTable, vert_params, he_params, face_params = outer_eq_prop(
vertTable,
heTable,
faceTable,
@@ -1406,7 +1458,7 @@ def bilevel_opt(
)
case BilevelOptimizationMethod.IMPLICIT_DIFFERENTIATION:
- vert_params, he_params, face_params = outer_implicit(
+ vertTable, heTable, faceTable, vert_params, he_params, face_params = outer_implicit(
vertTable,
heTable,
faceTable,
@@ -1434,7 +1486,7 @@ def bilevel_opt(
)
case BilevelOptimizationMethod.ADJOINT_STATE:
- vert_params, he_params, face_params = outer_adjoint_state(
+ vertTable, heTable, faceTable, vert_params, he_params, face_params = outer_adjoint_state(
vertTable,
heTable,
faceTable,
diff --git a/src/vertax/pbc.py b/src/vertax/pbc.py
deleted file mode 100644
index dba4219..0000000
--- a/src/vertax/pbc.py
+++ /dev/null
@@ -1,1266 +0,0 @@
-"""Periodic Boundary Condition on a mesh."""
-
-from collections.abc import Callable
-from pathlib import Path
-from typing import Literal, Self
-
-import jax
-import jax.numpy as jnp
-import matplotlib
-import matplotlib.pyplot as plt
-import numpy as np
-from jax import Array
-from matplotlib.axes import Axes
-from matplotlib.figure import Figure
-from numpy.typing import NDArray
-from scipy.spatial import Voronoi
-
-from vertax.geo import get_area, get_length, get_length_with_offset, get_perimeter, update_pbc
-from vertax.mask_analysis import find_vertices_edges_faces, mask_from_image, pad
-from vertax.mesh import Mesh
-from vertax.opt import (
- BilevelOptimizationMethod,
- InnerLossFunction,
- OuterLossFunction,
- UpdateT1Func,
- inner_opt,
- outer_adjoint_state,
- outer_eq_prop,
- outer_implicit,
- outer_opt,
-)
-from vertax.plot import EdgePlot, FacePlot, VertexPlot, add_colorbar, adjust_lightness, get_cmap
-from vertax.topo import do_not_update_T1, update_T1
-
-
-class PbcMesh(Mesh):
- """Periodic Boundary Condition on a mesh."""
-
- def __init__(self) -> None:
- """Do not call the constructor."""
- super().__init__()
-
- self.MAX_EDGES_IN_ANY_FACE: int = 20
- self._update_T1_func: UpdateT1Func = update_T1
-
- @classmethod
- def copy_mesh(cls, other_mesh: Self) -> Self:
- """Copy all parameters from another mesh in a new mesh."""
- mesh = cls._create()
- mesh.vertices = other_mesh.vertices.copy()
- mesh.edges = other_mesh.edges.copy()
- mesh.faces = other_mesh.faces.copy()
- mesh.width = other_mesh.width
- mesh.height = other_mesh.height
- mesh.vertices_params = other_mesh.vertices_params.copy()
- mesh.edges_params = other_mesh.edges_params.copy()
- mesh.faces_params = other_mesh.faces_params.copy()
- mesh.vertices_target = other_mesh.vertices_target.copy()
- mesh.edges_target = other_mesh.edges_target.copy()
- mesh.faces_target = other_mesh.faces_target.copy()
- mesh.image_target = other_mesh.image_target.copy()
- mesh.bilevel_optimization_method = other_mesh.bilevel_optimization_method
- mesh.beta = other_mesh.beta
- mesh.min_dist_T1 = other_mesh.min_dist_T1
- mesh.max_nb_iterations = other_mesh.max_nb_iterations
- mesh.tolerance = other_mesh.tolerance
- mesh.patience = other_mesh.patience
- mesh.inner_solver = other_mesh.inner_solver
- mesh.outer_solver = other_mesh.outer_solver
- mesh.MAX_EDGES_IN_ANY_FACE = other_mesh.MAX_EDGES_IN_ANY_FACE
- mesh._update_T1_func = other_mesh._update_T1_func
-
- return mesh
-
- def save_mesh_txt(
- self,
- directory: str,
- vertices_filename: str = "vertTable.txt",
- edges_filename: str = "heTable.txt",
- faces_filename: str = "faceTable.txt",
- ) -> None:
- """Save a mesh in separate text files that can be read by numpy.
-
- Only save the vertices, edges and faces, not other parameters.
-
- Args:
- directory (str): Path to the directory where to save the files.
- vertices_filename (str, optional): Filename for the vertices table. Defaults to "vertTable.txt".
- edges_filename (str, optional): Filename for the half-edges table. Defaults to "heTable.txt".
- faces_filename (str, optional): Filename for the faces table. Defaults to "faceTable.txt".
- """
- dirpath = Path(directory)
- dirpath.mkdir(parents=True, exist_ok=True)
- vertpath = dirpath / vertices_filename
- hepath = dirpath / edges_filename
- facepath = dirpath / faces_filename
- np.savetxt(vertpath, self.vertices)
- np.savetxt(hepath, self.edges)
- np.savetxt(facepath, self.faces)
-
- def save_mesh(self, path: str) -> None:
- """Save mesh to a file.
-
- All PBCMesh data is saved, except for the solvers that are not saved.
-
- Args:
- path (str): Path to the saved file. The extension is .npz, a numpy format.
- """
- Path(path).parent.mkdir(parents=True, exist_ok=True)
- np.savez_compressed(
- path,
- allow_pickle=False,
- vertices=self.vertices,
- edges=self.edges,
- faces=self.faces,
- width=self.width,
- height=self.height,
- vertices_params=self.vertices_params,
- edges_params=self.edges_params,
- faces_params=self.faces_params,
- vertices_target=self.vertices_target,
- edges_target=self.edges_target,
- faces_target=self.faces_target,
- image_target=self.image_target,
- bilevel_optimization_method=self.bilevel_optimization_method.value,
- beta=self.beta,
- min_dist_T1=self.min_dist_T1,
- max_nb_iterations=self.max_nb_iterations,
- tolerance=self.tolerance,
- patience=self.patience,
- MAX_EDGES_IN_ANY_FACE=self.MAX_EDGES_IN_ANY_FACE,
- update_t1=self.update_t1,
- )
-
- @classmethod
- def load_mesh(cls, path: str) -> Self:
- """Load a mesh from a file.
-
- All PBCMesh data is reloaded, except for the solvers that are not saved.
-
- Args:
- path (str): Path to the mesh file (.npz), numpy format.
-
- Returns:
- Mesh: the mesh loaded from the numpy .npz file.
- """
- mesh_file = np.load(path)
- mesh = cls._create()
- mesh.vertices, mesh.edges, mesh.faces = mesh_file["vertices"], mesh_file["edges"], mesh_file["faces"]
- mesh.width, mesh.height = mesh_file["width"], mesh_file["height"]
- mesh.vertices_params = mesh_file["vertices_params"]
- mesh.edges_params = mesh_file["edges_params"]
- mesh.faces_params = mesh_file["faces_params"]
- mesh.vertices_target = mesh_file["vertices_target"]
- mesh.edges_target = mesh_file["edges_target"]
- mesh.faces_target = mesh_file["faces_target"]
- mesh.image_target = mesh_file["image_target"]
- mesh.bilevel_optimization_method = BilevelOptimizationMethod(mesh_file["bilevel_optimization_method"])
- mesh.beta = mesh_file["beta"]
- mesh.min_dist_T1 = mesh_file["min_dist_T1"]
- mesh.max_nb_iterations = mesh_file["max_nb_iterations"]
- mesh.tolerance = mesh_file["tolerance"]
- mesh.patience = mesh_file["patience"]
- mesh.MAX_EDGES_IN_ANY_FACE = mesh_file["MAX_EDGES_IN_ANY_FACE"]
- mesh.update_t1 = mesh_file["update_t1"]
- return mesh
-
- @classmethod
- def load_mesh_txt(
- cls,
- directory: str,
- vertices_filename: str = "vertTable.txt",
- edges_filename: str = "heTable.txt",
- faces_filename: str = "faceTable.txt",
- ) -> Self:
- """Load a mesh from text files.
-
- Only load the vertices, edges and faces, not other parameters.
-
- Args:
- directory (str): Directory where the text files are stored.
- vertices_filename (str, optional): Filename for the vertices table. Defaults to "vertTable.txt".
- edges_filename (str, optional): Filename for the half-edges table. Defaults to "heTable.txt".
- faces_filename (str, optional): Filename for the faces table. Defaults to "faceTable.txt".
-
- Returns:
- Self: The loaded mesh.
- """
- dirpath = Path(directory)
- dirpath.mkdir(parents=True, exist_ok=True)
- vertpath = dirpath / vertices_filename
- hepath = dirpath / edges_filename
- facepath = dirpath / faces_filename
-
- mesh = cls._create()
- mesh.vertices = jnp.array(np.loadtxt(vertpath, dtype=np.float64))
- mesh.edges = jnp.array(np.loadtxt(hepath, dtype=np.int64))
- mesh.faces = jnp.array(np.loadtxt(facepath, dtype=np.int64))
- return mesh
-
- def get_length(self, half_edge_id: Array) -> Array:
- """Get the length of an edge."""
-
- def _get_length(half_edge_id: Array) -> Array:
- return get_length(half_edge_id, self.vertices, self.edges, self.faces, self.width, self.height)
-
- return jax.vmap(_get_length)(half_edge_id)
-
- def get_length_with_offset(self, half_edge_id: Array) -> Array:
- """Get the length of an edge along with its offsets in an array (length, offset x, offset y)."""
-
- def _get_length_with_offset(half_edge_id: Array) -> Array:
- return get_length_with_offset(half_edge_id, self.vertices, self.edges, self.faces, self.width, self.height)
-
- return jax.vmap(_get_length_with_offset)(half_edge_id)
-
- def get_perimeter(self, face_id: Array) -> Array:
- """Get the perimeter of a face."""
-
- def _get_perimeter(face_id: Array) -> Array:
- return get_perimeter(
- face_id, self.vertices, self.edges, self.faces, self.width, self.height, self.MAX_EDGES_IN_ANY_FACE
- )
-
- return jax.vmap(_get_perimeter)(face_id)
-
- def get_area(self, face_id: Array) -> Array:
- """Get the area of a face."""
-
- def _get_area(face_id: Array) -> Array:
- return get_area(
- face_id, self.vertices, self.edges, self.faces, self.width, self.height, self.MAX_EDGES_IN_ANY_FACE
- )
-
- return jax.vmap(_get_area)(face_id)
-
- def update_boundary_conditions(self) -> None:
- """Force periodic boundary conditions again after an update."""
- self.vertices, self.edges, self.faces = update_pbc(
- self.vertices, self.edges, self.faces, self.width, self.height
- )
-
- @property
- def update_t1(self) -> bool:
- """Whether or not update the mesh by applying T1 transitions."""
- return self._update_T1_func != do_not_update_T1
-
- @update_t1.setter
- def update_t1(self, b: bool) -> None:
- """Whether or not update the mesh by applying T1 transitions."""
- if b:
- self._update_T1_func = update_T1
- else:
- self._update_T1_func = do_not_update_T1
-
- def inner_opt(
- self,
- loss_function_inner: InnerLossFunction,
- only_on_vertices: None | list[int] = None,
- only_on_edges: None | list[int] = None,
- only_on_faces: None | list[int] = None,
- ) -> list[float]:
- """Optimize the mesh for the loss function given.
-
- Args:
- loss_function_inner (InnerLossFunction): Loss function to optimize.
- only_on_vertices (None | list[int] = None): Consider only a subset of vertices for the loss function.
- All vertices if None.
- only_on_edges (None | list[int] = None): Consider only a subset of edges for the loss function.
- All edges if None.
- only_on_faces (None | list[int] = None): Consider only a subset of faces for the loss function.
- All faces if None.
-
- Returns:
- list[float]: History of loss values during optimization.
- """
- selected_vertices, selected_edges, selected_faces = None, None, None
- if only_on_vertices is not None:
- selected_vertices = jnp.array(only_on_vertices)
- if only_on_edges is not None:
- selected_edges = jnp.array(only_on_edges)
- if only_on_faces is not None:
- selected_faces = jnp.array(only_on_faces)
-
- (self.vertices, self.edges, self.faces), loss_history = inner_opt(
- vertTable=self.vertices,
- heTable=self.edges,
- faceTable=self.faces,
- width=self.width,
- height=self.height,
- vert_params=self.vertices_params,
- he_params=self.edges_params,
- face_params=self.faces_params,
- L_in=loss_function_inner,
- solver=self.inner_solver,
- min_dist_T1=self.min_dist_T1,
- iterations_max=self.max_nb_iterations,
- tolerance=self.tolerance,
- patience=self.patience,
- selected_verts=selected_vertices,
- selected_hes=selected_edges,
- selected_faces=selected_faces,
- update_t1_func=self._update_T1_func,
- )
- return list(loss_history)
-
- def outer_opt(
- self,
- loss_function_inner: InnerLossFunction,
- loss_function_outer: OuterLossFunction,
- only_on_vertices: None | list[int] = None,
- only_on_edges: None | list[int] = None,
- only_on_faces: None | list[int] = None,
- ) -> None:
- """Outer optimization."""
- selected_vertices, selected_edges, selected_faces = None, None, None
- if only_on_vertices is not None:
- selected_vertices = jnp.array(only_on_vertices)
- if only_on_edges is not None:
- selected_edges = jnp.array(only_on_edges)
- if only_on_faces is not None:
- selected_faces = jnp.array(only_on_faces)
- match self.bilevel_optimization_method:
- case BilevelOptimizationMethod.AUTOMATIC_DIFFERENTIATION:
- self.vertices_params, self.edges_params, self.faces_params = outer_opt(
- self.vertices,
- self.edges,
- self.faces,
- self.width,
- self.height,
- self.vertices_params,
- self.edges_params,
- self.faces_params,
- self.vertices_target,
- self.edges_target,
- self.faces_target,
- loss_function_inner,
- loss_function_outer,
- self.inner_solver,
- self.outer_solver,
- self.min_dist_T1,
- self.max_nb_iterations,
- self.tolerance,
- self.patience,
- selected_vertices,
- selected_edges,
- selected_faces,
- self.image_target,
- self._update_T1_func,
- )
-
- case BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION:
- self.vertices_params, self.edges_params, self.faces_params = outer_eq_prop(
- self.vertices,
- self.edges,
- self.faces,
- self.width,
- self.height,
- self.vertices_params,
- self.edges_params,
- self.faces_params,
- self.vertices_target,
- self.edges_target,
- self.faces_target,
- loss_function_inner,
- loss_function_outer,
- self.inner_solver,
- self.outer_solver,
- self.min_dist_T1,
- self.max_nb_iterations,
- self.tolerance,
- self.patience,
- selected_vertices,
- selected_edges,
- selected_faces,
- self.image_target,
- self.beta,
- self._update_T1_func,
- )
-
- case BilevelOptimizationMethod.IMPLICIT_DIFFERENTIATION:
- self.vertices_params, self.edges_params, self.faces_params = outer_implicit(
- self.vertices,
- self.edges,
- self.faces,
- self.width,
- self.height,
- self.vertices_params,
- self.edges_params,
- self.faces_params,
- self.vertices_target,
- self.edges_target,
- self.faces_target,
- loss_function_inner,
- loss_function_outer,
- self.inner_solver,
- self.outer_solver,
- self.min_dist_T1,
- self.max_nb_iterations,
- self.tolerance,
- self.patience,
- selected_vertices,
- selected_edges,
- selected_faces,
- self.image_target,
- self._update_T1_func,
- )
-
- case BilevelOptimizationMethod.ADJOINT_STATE:
- self.vertices_params, self.edges_params, self.faces_params = outer_adjoint_state(
- self.vertices,
- self.edges,
- self.faces,
- self.width,
- self.height,
- self.vertices_params,
- self.edges_params,
- self.faces_params,
- self.vertices_target,
- self.edges_target,
- self.faces_target,
- loss_function_inner,
- loss_function_outer,
- self.inner_solver,
- self.outer_solver,
- self.min_dist_T1,
- self.max_nb_iterations,
- self.tolerance,
- self.patience,
- selected_vertices,
- selected_edges,
- selected_faces,
- self.image_target,
- self._update_T1_func,
- )
-
- case _:
- msg = f"Method not recognized. Must be a BilevelOptimizationMethod. \
- Got {self.bilevel_optimization_method}."
- raise ValueError(msg)
-
- def bilevel_opt(
- self,
- loss_function_inner: InnerLossFunction,
- loss_function_outer: OuterLossFunction,
- only_on_vertices: None | list[int] = None,
- only_on_edges: None | list[int] = None,
- only_on_faces: None | list[int] = None,
- ) -> list[float]:
- """Optimize the mesh for the loss function given.
-
- Args:
- loss_function_inner (InnerLossFunction): Loss function to optimize.
- loss_function_outer (OuterLossFunction): Loss function to optimize.
- only_on_vertices (None | list[int] = None): Consider only a subset of vertices for the loss function.
- All vertices if None.
- only_on_edges (None | list[int] = None): Consider only a subset of edges for the loss function.
- All edges if None.
- only_on_faces (None | list[int] = None): Consider only a subset of faces for the loss function.
- All faces if None.
-
- Returns:
- list[float]: History of loss values during optimization.
- """
- self.outer_opt(loss_function_inner, loss_function_outer, only_on_vertices, only_on_edges, only_on_faces)
- return self.inner_opt(loss_function_inner, only_on_vertices, only_on_edges, only_on_faces)
-
- @classmethod
- def periodic_voronoi_from_random_seeds(cls, nb_seeds: int, width: float, height: float, random_key: int) -> Self:
- """Create a Periodic Voronoi Mesh from random seeds.
-
- Args:
- nb_seeds (int): Number of random seeds to use.
- width (float): Width of the rectangular domains the seeds will be in.
- height (float): Height of the rectangular domains the seeds will be in.
- random_key (int): Set the random key for reproducibility.
-
- Returns:
- Self: The corresponding mesh.
- """
- key = jax.random.PRNGKey(random_key)
- seeds = jnp.array((width, height)) * jax.random.uniform(key, (nb_seeds, 2))
- return cls.periodic_voronoi_from_seeds(seeds, width, height)
-
- @classmethod
- def periodic_voronoi_from_seeds(cls, seeds: Array, width: float, height: float) -> Self:
- """Create a Periodic Voronoi Mesh from a list of seeds.
-
- The seeds are assumed to have positive x and y positions.
-
- Args:
- seeds (Array[float32]): jax float array of seed positions of shape (nbSeeds, 2).
- width (float): width of the box containing the seeds.
- height (float): height of the box containing the seeds.
- """
- (
- periodic_voronoi_vertices_idx,
- periodic_voronoi_vertices_pos,
- periodic_voronoi_edges,
- offsets,
- periodic_voronoi_faces,
- ) = _make_periodic(seeds, width, height)
-
- vertices, edges, faces = _make_he_structure(
- width,
- height,
- periodic_voronoi_vertices_idx,
- periodic_voronoi_vertices_pos,
- periodic_voronoi_edges,
- offsets,
- periodic_voronoi_faces,
- )
-
- pbc_mesh = cls._create()
- pbc_mesh.vertices = jnp.array(vertices, dtype=np.float32)
- pbc_mesh.edges = jnp.array(edges, dtype=np.int32)
- pbc_mesh.faces = jnp.array(faces, dtype=np.int32)
- pbc_mesh.width = width
- pbc_mesh.height = height
-
- return pbc_mesh
-
- @classmethod
- def periodic_from_image(
- cls,
- image: NDArray,
- ) -> Self:
- """Create a rudimentary mesh with periodic boundary conditions from an image.
-
- To do that, we perform a segmentation using Cellpose and we try to fill the holes.
- The result will probably be imperfect and it will always be better if you
- provide directly a mask (with no holes) with the function "periodic_from_mask".
-
- Args:
- image (NDArray): The image which will act as a template for the mesh.
-
- Returns:
- tuple[Array, Array, Array]: The vertices, half-edges and faces table of the mesh.
- """
- return cls.periodic_from_mask(mask_from_image(image))
-
- @classmethod
- def periodic_from_mask( # noqa: C901
- cls,
- mask: NDArray,
- ) -> Self:
- """Create a rudimentary mesh with periodic boundary conditions from a mask with no holes.
-
- Args:
- mask (NDArray): The mask with no holes which will act as a template for the mesh.
-
- Returns:
- tuple[Array, Array, Array]: The vertices, half-edges and faces table of the mesh.
- """
- padded_mask = pad(mask, save=False, output_path="refined_and_padded_image.tiff")
- # Find vertices, edges, faces
- vertices, edges, faces = find_vertices_edges_faces(padded_mask)
-
- # imread tiff = Y is the first axis, X the second.
- height: int = mask.shape[0] # original image length. Padded is 3 times bigger.
- y_min = height / 2
- y_max = 2 * height + (height / 2)
- width: int = mask.shape[1]
- x_min = width / 2
- x_max = 2 * width + (width / 2)
-
- col0_mask = (vertices[:, 0] >= x_min) & (vertices[:, 0] < x_max)
- col1_mask = (vertices[:, 1] >= y_min) & (vertices[:, 1] < y_max)
-
- periodic_vertices_idx = np.arange(len(vertices))[col0_mask & col1_mask]
- periodic_vertices_pos = vertices[col0_mask & col1_mask]
-
- # store map between vertex id -> inside vertex id
- inside_vertex: dict[int, int] = {idx: idx for idx in periodic_vertices_idx}
- for i, vertex in enumerate(vertices):
- if i not in periodic_vertices_idx:
- x, y = vertex
- if x < x_min:
- x += 2 * width
- elif x >= x_max:
- x -= 2 * width
-
- if y < y_min:
- y += 2 * height
- elif y >= y_max:
- y -= 2 * height
-
- # Find corresponding inside vertex to the outside dest vertex
- for idx, pos in zip(periodic_vertices_idx, periodic_vertices_pos, strict=True):
- if np.max(np.abs(pos - [x, y])) < 1:
- inside_vertex[i] = idx
- break
-
- edges_inside = []
- edges_outside = []
- offsets_inside = {}
- offsets_outside = {}
- visited = []
-
- for e in edges:
- if e[0] in periodic_vertices_idx and e[1] in periodic_vertices_idx:
- edges_inside.append(tuple(sorted((e[0], e[1]))))
- offsets_inside[(e[0], e[1])] = (0, 0)
- offsets_inside[(e[1], e[0])] = (0, 0)
- elif bool(e[0] in periodic_vertices_idx) != bool(e[1] in periodic_vertices_idx):
- if e[0] in periodic_vertices_idx:
- # origin in, dest out
- # check x coord
- if vertices[e[1]][0] < x_min:
- offset_x1 = -1
- elif vertices[e[1]][0] >= x_max:
- offset_x1 = 1
- else:
- offset_x1 = 0
-
- # Now check y coord
- if vertices[e[1]][1] < y_min:
- offset_y1 = -1
- elif vertices[e[1]][1] >= y_max:
- offset_y1 = 1
- else:
- offset_y1 = 0
-
- # Find corresponding inside vertex to the outside dest vertex
- if e[1] not in inside_vertex:
- print(f"Error, no inside vertex found for vertex {e[1]}.")
- else:
- idx = inside_vertex[e[1]]
- edges_outside.append(tuple(sorted((e[0], idx))))
- if (e[0], e[1]) not in visited and (e[1], e[0]) not in visited:
- offsets_outside[(e[0], idx)] = (offset_x1, offset_y1)
- offsets_outside[(idx, e[0])] = (-offset_x1, -offset_y1)
- visited.append((e[0], e[1]))
- visited.append((e[1], e[0]))
- else:
- # dest in, origin out
- if vertices[e[0]][0] < x_min:
- offset_x0 = -1
- elif vertices[e[0]][0] >= x_max:
- offset_x0 = 1
- else:
- offset_x0 = 0
-
- if vertices[e[0]][1] < y_min:
- offset_y0 = -1
- elif vertices[e[0]][1] >= y_max:
- offset_y0 = 1
- else:
- offset_y0 = 0
-
- # Find corresponding inside vertex to the outside dest vertex
- if e[0] not in inside_vertex:
- print(f"Error, no inside vertex found for vertex {e[0]}.")
- else:
- idx = inside_vertex[e[0]]
- edges_outside.append(tuple(sorted((idx, e[1]))))
- if (e[0], e[1]) not in visited and (e[1], e[0]) not in visited:
- offsets_outside[(idx, e[1])] = (-offset_x0, -offset_y0)
- offsets_outside[(e[1], idx)] = (offset_x0, offset_y0)
- visited.append((e[0], e[1]))
- visited.append((e[1], e[0]))
-
- periodic_edges = list(set(edges_inside)) + list(set(edges_outside))
- offsets = offsets_inside | offsets_outside
-
- periodic_faces: list[set[int]] = [
- {inside_vertex[i] for i in face} for face in faces if any(v_id in periodic_vertices_idx for v_id in face)
- ]
-
- vertices, edges, faces = _make_he_structure(
- 2 * width,
- 2 * height,
- periodic_vertices_idx, # ty:ignore[invalid-argument-type]
- periodic_vertices_pos,
- periodic_edges,
- offsets,
- periodic_faces,
- vertices_offset=(x_min, y_min),
- )
-
- pbc_mesh = cls._create()
- pbc_mesh.vertices = jnp.array(vertices, dtype=np.float32)
- pbc_mesh.edges = jnp.array(edges, dtype=np.int32)
- pbc_mesh.faces = jnp.array(faces, dtype=np.int32)
- pbc_mesh.width = 2 * width
- pbc_mesh.height = 2 * height
-
- return pbc_mesh
-
- def plot(
- self,
- vertex_plot: VertexPlot = VertexPlot.BLACK,
- edge_plot: EdgePlot = EdgePlot.BLACK,
- face_plot: FacePlot = FacePlot.MULTICOLOR,
- vertex_parameters_name: str = "",
- edge_parameters_name: str = "",
- face_parameters_name: str = "",
- show: bool = True,
- save: bool = False,
- save_path: str = "pbc_mesh.png",
- faces_cmap_name: str = "cividis",
- edges_cmap_name: str = "coolwarm",
- edges_width: float = 2,
- vertices_cmap_name: str = "spring",
- vertices_size: float = 20,
- title: str = "",
- ) -> None:
- """Plot the mesh and decide to save and/or show the mesh or not."""
- fig, _ax = self.get_plot(
- vertex_plot,
- edge_plot,
- face_plot,
- vertex_parameters_name,
- edge_parameters_name,
- face_parameters_name,
- faces_cmap_name,
- edges_cmap_name,
- edges_width,
- vertices_cmap_name,
- vertices_size,
- title,
- )
-
- if save:
- plt.savefig(save_path)
-
- if show:
- plt.show()
-
- plt.close(fig)
-
- def get_plot(
- self,
- vertex_plot: VertexPlot = VertexPlot.BLACK,
- edge_plot: EdgePlot = EdgePlot.BLACK,
- face_plot: FacePlot = FacePlot.MULTICOLOR,
- vertex_parameters_name: str = "",
- edge_parameters_name: str = "",
- face_parameters_name: str = "",
- faces_cmap_name: str = "cividis",
- edges_cmap_name: str = "coolwarm",
- edges_width: float = 2,
- vertices_cmap_name: str = "spring",
- vertices_size: float = 20,
- title: str = "",
- ) -> tuple[Figure, Axes]:
- """Get the matplotlib figure and and ax for one plot."""
- # Fates not used for pbc.
- if face_plot == FacePlot.FATES:
- face_plot = FacePlot.WHITE
-
- fig, _ = plt.subplots(layout="constrained")
- ax = plt.gca()
- self._plot_faces(fig, ax, face_plot, faces_cmap_name, face_parameters_name)
- self._plot_edges(fig, ax, edge_plot, edges_cmap_name, edges_width, edge_parameters_name)
- self._plot_vertices(fig, ax, vertex_plot, vertices_cmap_name, vertices_size, vertex_parameters_name)
-
- ax.set_title(title)
- ax.set_aspect(self.height / self.width)
- ax.set_xlim(0, self.width)
- ax.set_ylim(0, self.height)
-
- return fig, ax
-
- def _plot_faces(
- self, fig: Figure, ax: Axes, face_plot: FacePlot, faces_cmap_name: str, face_parameters_name: str
- ) -> None:
- multicolor_cmap = self._get_multicolor_face_cmap()
- faces_cmap = matplotlib.colormaps.get_cmap(faces_cmap_name)
-
- v_max = 1
- v_min = 0
- values = jnp.array([1])
- # set the correct colorbar if needed
- # Get values, min and max
- cbar_label = "Face parameter" if face_parameters_name == "" else face_parameters_name
- match face_plot:
- case FacePlot.FACE_PARAMETER:
- values = self.faces_params
- case FacePlot.AREA:
- values = self.get_area(jnp.arange(self.nb_faces))
- cbar_label = "Area of cell"
- case FacePlot.PERIMETER:
- values = self.get_perimeter(jnp.arange(self.nb_faces))
- cbar_label = "Perimeter of cell"
- v_max = float(values.max())
- v_min = float(values.min())
- match face_plot:
- case FacePlot.MULTICOLOR | FacePlot.WHITE:
- pass
- case _:
- cbar = add_colorbar(fig, ax, v_min, v_max, faces_cmap)
- cbar.ax.set_ylabel(cbar_label, rotation=270, labelpad=13)
- cbar.ax.yaxis.set_ticks_position("left")
-
- # Draw each face
- for face in range(len(self.faces)):
- match face_plot:
- # Find correct color depending on chosen colormap
- case FacePlot.MULTICOLOR:
- color = multicolor_cmap(face)
- case FacePlot.WHITE:
- color = (1, 1, 1, 1)
- case _:
- norm_val = 1 if v_max == v_min else (values[face] - v_min) / (v_max - v_min)
- color = faces_cmap(norm_val)
- # Find face's vertices and draw the corresponding polygon.
- face_vertices = self._get_face_vertices(face)
- self._draw_face(ax, face_vertices, color)
-
- def _get_face_vertices(self, face: int) -> NDArray:
- """Get all vertices positions corresponding to a face."""
- start_he = self.faces[face]
- he = start_he
-
- source_vertex_id = self.edges[he][3]
- verts_sources = np.array([self.vertices[source_vertex_id]])
- # all_verts.append(self.vertices[v_source])
-
- he_offset_x = self.edges[he][6]
- he_offset_y = self.edges[he][7]
- sum0_offsets = he_offset_x
- sum1_offsets = he_offset_y
-
- he = self.edges[he][1]
-
- while he != start_he:
- source_vertex_id = self.edges[he][3]
-
- # all_verts.append(self.vertices[v_source])
-
- verts_sources = np.concatenate(
- (
- verts_sources,
- (
- np.array([self.vertices[source_vertex_id]])
- + np.array([sum0_offsets * self.width, sum1_offsets * self.height])
- ),
- ),
- axis=0,
- )
-
- he_offset_x = self.edges[he][6]
- he_offset_y = self.edges[he][7]
- sum0_offsets += he_offset_x
- sum1_offsets += he_offset_y
-
- he = self.edges[he][1]
-
- source_vertex_id = self.edges[he][3]
- verts_sources = np.concatenate((verts_sources, (np.array([self.vertices[source_vertex_id]]))), axis=0)
- return verts_sources
-
- def _draw_face(self, ax: Axes, face_vertices: NDArray, color: tuple[float, float, float, float] | NDArray) -> None:
- x = face_vertices[:, 0]
- y = self.height - face_vertices[:, 1]
-
- ax.fill(x, y, color=color)
- ax.fill(np.add(x, self.width), np.add(y, self.height), color=color)
- ax.fill(np.add(x, -self.width), np.add(y, -self.height), color=color)
- ax.fill(np.add(x, -self.width), np.add(y, self.height), color=color)
- ax.fill(np.add(x, self.width), np.add(y, -self.height), color=color)
- ax.fill(x, np.add(y, self.height), color=color)
- ax.fill(x, np.add(y, -self.height), color=color)
- ax.fill(np.add(x, self.width), y, color=color)
- ax.fill(np.add(x, -self.width), y, color=color)
-
- def _get_multicolor_face_cmap(self) -> Callable[[int], tuple[float, float, float, float]]:
- def cmap_light_hsv(n: int) -> Callable[[int], tuple[float, float, float, Literal[1]]]:
- def light_hsv(i: int) -> tuple[float, float, float, Literal[1]]:
- fun: Callable[[int], tuple[float, float, float, float]] = get_cmap(n, name="hsv")
- return (*adjust_lightness(fun(i)[:3], 1.4), 1)
-
- return light_hsv
-
- return cmap_light_hsv(len(self.faces))
-
- def _plot_edges(
- self,
- fig: Figure,
- ax: Axes,
- edge_plot: EdgePlot,
- edges_cmap_name: str,
- edges_width: float,
- edge_parameters_name: str,
- ) -> None:
- if edge_plot != EdgePlot.INVISIBLE:
- edge_params_cmap = matplotlib.colormaps.get_cmap(edges_cmap_name)
- # set the correct colorbar
-
- v_max = 1
- v_min = 0
- values = jnp.array([1])
- # set the correct colorbar if needed
- # Get values, min and max
- cbar_label = "Edge parameter" if edge_parameters_name == "" else edge_parameters_name
- match edge_plot:
- case EdgePlot.EDGE_PARAMETER:
- values = self.edges_params
- case EdgePlot.LENGTH:
- values = self.get_length(jnp.arange(2 * self.nb_edges))
- cbar_label = "Length of edge"
- v_max = float(values.max())
- v_min = float(values.min())
-
- match edge_plot:
- case EdgePlot.BLACK:
- pass
- case _:
- cbar = add_colorbar(fig, ax, v_min, v_max, edge_params_cmap)
- cbar.ax.set_ylabel(cbar_label, rotation=270, labelpad=13)
- cbar.ax.yaxis.set_ticks_position("left")
-
- # Draw each edge
- for i, edge_entry in enumerate(self.edges):
- # Find correct color depending on chosen colormap
- match edge_plot:
- case EdgePlot.BLACK:
- color = (0, 0, 0, 1)
- case _:
- norm_val = 1 if v_max == v_min else (values[i] - v_min) / (v_max - v_min)
- color = edge_params_cmap(norm_val)
- # Draw the edge with color
- self._draw_edge(ax, edge_entry, color, edges_width)
-
- def _draw_edge(
- self,
- ax: Axes,
- edge_entry: tuple[int, int, int, int, int, int, int, int],
- color: tuple[float, float, float, float] | NDArray,
- edges_width: float,
- ) -> None:
- origin_id = edge_entry[3]
- target_id = edge_entry[4]
- he_offset_x = edge_entry[6]
- he_offset_y = edge_entry[7]
-
- origin = self.vertices[origin_id]
- target = self.vertices[target_id] + np.array([he_offset_x * self.width, he_offset_y * self.height])
-
- points = np.array([origin, target])
- x = points[:, 0]
- y = self.height - points[:, 1]
- ax.plot(x, y, color=color, linewidth=edges_width)
-
- def _plot_vertices(
- self,
- fig: Figure,
- ax: Axes,
- vertex_plot: VertexPlot,
- vertices_cmap_name: str,
- vertices_size: float,
- vertex_parameters_name: str,
- ) -> None:
- if vertex_plot != VertexPlot.INVISIBLE:
- # set the correct colorbar
- vertices_params_cmap = matplotlib.colormaps.get_cmap(vertices_cmap_name)
- v_max = 1
- v_min = 0
- if vertex_plot == VertexPlot.VERTEX_PARAMETER:
- v_max = float(self.vertices_params.max())
- v_min = float(self.vertices_params.min())
- cbar = add_colorbar(fig, ax, v_min, v_max, vertices_params_cmap)
- cbar_label = "Vertex parameter" if vertex_parameters_name == "" else vertex_parameters_name
- cbar.ax.set_ylabel(cbar_label, rotation=270, labelpad=13)
- cbar.ax.yaxis.set_ticks_position("left")
- # Draw each vertex
- for i, vertex in enumerate(self.vertices):
- # Find correct color depending on chosen colormap
- match vertex_plot:
- case VertexPlot.VERTEX_PARAMETER:
- norm_val = 1 if v_max == v_min else (self.vertices_params[i] - v_min) / (v_max - v_min)
- color = vertices_params_cmap(norm_val)
- case VertexPlot.BLACK:
- color = (0, 0, 0, 1)
- case _:
- color = (0, 0, 0, 0)
- # Draw the vertex with color
- ax.scatter(vertex[0], self.height - vertex[1], color=color, s=vertices_size)
-
-
-def _make_periodic( # noqa: C901
- seeds: Array,
- width: float,
- height: float,
-) -> tuple[
- NDArray[np.int32],
- NDArray[np.float64],
- list[tuple[int, int]],
- dict[tuple[int, int], tuple[int, int]],
- list[set[int]],
-]:
- n_cells = len(seeds)
-
- # PERIODIC VORONOI - VERTICES EDGES FACES
-
- if n_cells < 20:
- print("\nWarning: [n_cells < 20] initial condition may not work as expected.\n")
-
- # add eight neighbor copies of the seeds
- padded_seeds = np.concatenate(
- (
- seeds,
- np.add(seeds, np.full((n_cells, 2), [-width, +height])),
- np.add(seeds, np.full((n_cells, 2), [0, +height])),
- np.add(seeds, np.full((n_cells, 2), [width, +height])),
- np.add(seeds, np.full((n_cells, 2), [-width, 0])),
- np.add(seeds, np.full((n_cells, 2), [width, 0])),
- np.add(seeds, np.full((n_cells, 2), [-width, -height])),
- np.add(seeds, np.full((n_cells, 2), [0, -height])),
- np.add(seeds, np.full((n_cells, 2), [width, -height])),
- ),
- axis=0,
- )
-
- voronoi = Voronoi(padded_seeds)
-
- vertices = voronoi.vertices
- edges = voronoi.ridge_vertices
- faces = voronoi.regions
-
- # original vertices and not from neighbor copies
- col0_mask = (vertices[:, 0] >= 0.0) & (vertices[:, 0] <= width)
- col1_mask = (vertices[:, 1] >= 0.0) & (vertices[:, 1] <= height)
-
- periodic_voronoi_vertices_idx: NDArray[np.int32] = np.arange(len(vertices))[col0_mask & col1_mask] # ty:ignore[invalid-assignment]
- periodic_voronoi_vertices_pos: NDArray[np.float64] = vertices[col0_mask & col1_mask]
-
- edges_inside = []
- edges_outside = []
- offsets_inside = {}
- offsets_outside = {}
- visited = []
-
- for e in edges:
- source_in = e[0] in periodic_voronoi_vertices_idx
- target_in = e[1] in periodic_voronoi_vertices_idx
- if source_in and target_in:
- edges_inside.append(tuple(sorted((e[0], e[1]))))
- offsets_inside[(e[0], e[1])] = (0, 0)
- offsets_inside[(e[1], e[0])] = (0, 0)
- elif source_in: # and not target_in
- if vertices[e[1]][0] < 0.0:
- x = vertices[e[1]][0] + width
- offset_x1 = -1
- elif vertices[e[1]][0] > width:
- x = vertices[e[1]][0] - width
- offset_x1 = 1
- else:
- x = vertices[e[1]][0]
- offset_x1 = 0
- if vertices[e[1]][1] < 0.0:
- y = vertices[e[1]][1] + height
- offset_y1 = -1
- elif vertices[e[1]][1] > height:
- y = vertices[e[1]][1] - height
- offset_y1 = 1
- else:
- y = vertices[e[1]][1]
- offset_y1 = 0
- for idx, pos in zip(periodic_voronoi_vertices_idx, periodic_voronoi_vertices_pos, strict=False):
- if ((np.abs(pos[0] - x)) < 10**-8) and ((np.abs(pos[1] - y)) < 10**-8):
- edges_outside.append(tuple(sorted((e[0], idx))))
- if (e[0], e[1]) not in visited and (e[1], e[0]) not in visited:
- offsets_outside[(e[0], idx)] = (offset_x1, offset_y1)
- offsets_outside[(idx, e[0])] = (-offset_x1, -offset_y1)
- visited.append((e[0], e[1]))
- visited.append((e[1], e[0]))
- break
- elif target_in: # and not source_in
- if vertices[e[0]][0] < 0.0:
- x = vertices[e[0]][0] + width
- offset_x0 = -1
- elif vertices[e[0]][0] > width:
- x = vertices[e[0]][0] - width
- offset_x0 = 1
- else:
- x = vertices[e[0]][0]
- offset_x0 = 0
- if vertices[e[0]][1] < 0.0:
- y = vertices[e[0]][1] + height
- offset_y0 = -1
- elif vertices[e[0]][1] > height:
- y = vertices[e[0]][1] - height
- offset_y0 = 1
- else:
- y = vertices[e[0]][1]
- offset_y0 = 0
- for idx, pos in zip(periodic_voronoi_vertices_idx, periodic_voronoi_vertices_pos, strict=False):
- if ((np.abs(pos[0] - x)) < 10**-8) and ((np.abs(pos[1] - y)) < 10**-8):
- edges_outside.append(tuple(sorted((idx, e[1]))))
- if (e[0], e[1]) not in visited and (e[1], e[0]) not in visited:
- offsets_outside[(idx, e[1])] = (-offset_x0, -offset_y0)
- offsets_outside[(e[1], idx)] = (offset_x0, offset_y0)
- visited.append((e[0], e[1]))
- visited.append((e[1], e[0]))
- break
-
- periodic_voronoi_edges: list[tuple[int, int]] = list(set(edges_inside)) + list(set(edges_outside))
-
- offsets: dict[tuple[int, int], tuple[int, int]] = offsets_inside | offsets_outside
-
- faces_inside = []
- faces_inside_outside = []
- for face in faces:
- if face:
- if all(item in periodic_voronoi_vertices_idx for item in face):
- faces_inside.append(tuple(sorted(face)))
- if any(item in face for item in periodic_voronoi_vertices_idx):
- face_inside_outside = []
- for f in face:
- if f in periodic_voronoi_vertices_idx:
- face_inside_outside.append(f)
- else:
- if vertices[f][0] < 0.0:
- x = vertices[f][0] + width
- elif vertices[f][0] > width:
- x = vertices[f][0] - width
- else:
- x = vertices[f][0]
- if vertices[f][1] < 0.0:
- y = vertices[f][1] + height
- elif vertices[f][1] > height:
- y = vertices[f][1] - height
- else:
- y = vertices[f][1]
- for idx, pos in zip(periodic_voronoi_vertices_idx, periodic_voronoi_vertices_pos, strict=False):
- if ((np.abs(pos[0] - x)) < 10**-8) and ((np.abs(pos[1] - y)) < 10**-8):
- face_inside_outside.append(idx)
- break
-
- faces_inside_outside.append(tuple(sorted(face_inside_outside)))
-
- periodic_voronoi_faces: list[set[int]] = list(set(faces_inside_outside))
- return (
- periodic_voronoi_vertices_idx,
- periodic_voronoi_vertices_pos,
- periodic_voronoi_edges,
- offsets,
- periodic_voronoi_faces,
- )
-
-
-def _make_he_structure( # noqa: C901
- width: float,
- height: float,
- periodic_vertices_idx: NDArray[np.int32],
- periodic_vertices_positions: NDArray[np.float64],
- periodic_edges: list[tuple[int, int]],
- offsets: dict[tuple[int, int], tuple[int, int]],
- periodic_faces: list[set[int]],
- vertices_offset: tuple[float, float] = (0, 0),
-) -> tuple[NDArray[np.float64], NDArray[np.int32], NDArray[np.int32]]:
- """Return half-edge structure with vertices, edges, faces.
-
- vertices is the position of vertices.
- edges is a table of int with:
- previous half-edge id in face,
- next half-edge id in face,
- twin half-edge id,
- source vertex id,
- target vertex id,
- face it belongs id,
- offset x,
- offset y.
- faces records just for each face the id of one of its edges.
- """
- # HALF EDGE DATA STRUCTURE
-
- # Reciprocating edges
- periodic_half_edges = []
- for e in periodic_edges:
- periodic_half_edges.append(e)
- periodic_half_edges.append((e[1], e[0]))
-
- # Finding clockwise (or counterclockwise) half edge set for each face
- ordered_edges_periodic_faces = []
- for face in periodic_faces:
- edges_face = [(f1, f2) for f1 in face for f2 in face if (f1, f2) in periodic_edges]
- i = 0
- start_edge = edges_face[i]
- ordered_face = [start_edge]
- e = start_edge
- visited = [e]
- while sorted(edges_face) != sorted(visited):
- if e[0] == start_edge[1] and e not in visited:
- ordered_face.append(e)
- start_edge = e
- visited.append(e)
- if e[1] == start_edge[1] and e not in visited:
- ordered_face.append((e[1], e[0]))
- start_edge = (e[1], e[0])
- visited.append(e)
- i += 1
- e = edges_face[i % len(face)]
-
- order = 0
- sum0_offsets = 0
- sum1_offsets = 0
- points = []
- for e in ordered_face:
- idx0 = list(periodic_vertices_idx).index(e[0])
- idx1 = list(periodic_vertices_idx).index(e[1])
- e_offsets = offsets[e]
-
- prev_sum0_offsets = sum0_offsets
- prev_sum1_offsets = sum1_offsets
- sum0_offsets += e_offsets[0]
- sum1_offsets += e_offsets[1]
-
- order += (
- (periodic_vertices_positions[idx1][0] + sum0_offsets * width)
- - (periodic_vertices_positions[idx0][0] + prev_sum0_offsets * width)
- ) * (
- (periodic_vertices_positions[idx1][1] + sum1_offsets * height)
- + (periodic_vertices_positions[idx0][1] + prev_sum1_offsets * height)
- )
-
- points.append(
- (
- periodic_vertices_positions[idx0][0] + prev_sum0_offsets * width,
- periodic_vertices_positions[idx0][1] + prev_sum1_offsets * height,
- )
- )
-
- points.append(
- (
- periodic_vertices_positions[idx1][0] + sum0_offsets * width,
- periodic_vertices_positions[idx1][1] + sum1_offsets * height,
- )
- )
-
- if order < 0:
- ordered_edges_periodic_faces.append(ordered_face)
- if order > 0:
- new_ordered_face = [(e[1], e[0]) for e in reversed(ordered_face)]
- ordered_edges_periodic_faces.append(new_ordered_face)
- if order == 0:
- print("\nError: no order detected for face " + str(face) + "\n")
- exit()
-
- # VERT FACE HE TABLES
-
- vertTable = periodic_vertices_positions - vertices_offset
-
- faceTable = np.zeros(len(periodic_faces), dtype=np.int32)
- for i, hedges_face in enumerate(ordered_edges_periodic_faces):
- for j, he in enumerate(periodic_half_edges):
- if he == hedges_face[0]:
- faceTable[i] = j # he_inside
-
- heTable = np.zeros((len(periodic_half_edges), 8), dtype=np.int32)
- for i, he in enumerate(periodic_half_edges):
- for hedges_face in ordered_edges_periodic_faces:
- if he in hedges_face:
- idx = hedges_face.index(he)
- heTable[i][0] = periodic_half_edges.index(hedges_face[(idx - 1) % len(hedges_face)]) # he_prev
- heTable[i][1] = periodic_half_edges.index(hedges_face[(idx + 1) % len(hedges_face)]) # he_next
- heTable[i][3] = list(periodic_vertices_idx).index(he[0]) # vert source
- heTable[i][4] = list(periodic_vertices_idx).index(he[1]) # vert target
- heTable[i][5] = ordered_edges_periodic_faces.index(hedges_face) # face
- break
- heTable[i][2] = periodic_half_edges.index((he[1], he[0])) # he twin
- heTable[i][6] = offsets[he][0] # he_offset x vert target
- heTable[i][7] = offsets[he][1] # he_offset y vert target
- return (vertTable, heTable, faceTable)
diff --git a/src/vertax/plot.py b/src/vertax/plot.py
deleted file mode 100644
index 94a7485..0000000
--- a/src/vertax/plot.py
+++ /dev/null
@@ -1,653 +0,0 @@
-"""General plotting functions."""
-
-import colorsys
-from collections.abc import Callable
-from enum import Enum
-from pathlib import Path
-
-import matplotlib
-import matplotlib.pyplot as plt
-import numpy as np
-from jax import Array
-from matplotlib.axes import Axes
-from matplotlib.colorbar import Colorbar
-from matplotlib.colors import Colormap, to_rgb
-from matplotlib.figure import Figure
-from matplotlib.patches import Arc
-from numpy.typing import NDArray
-
-
-class FacePlot(Enum):
- """What it is possible to show on a face."""
-
- MULTICOLOR = 1
- FACE_PARAMETER = 2
- AREA = 3
- PERIMETER = 4
- WHITE = 5
- FATES = 6
-
-
-class EdgePlot(Enum):
- """What it is possible to show on an edge."""
-
- BLACK = 1
- EDGE_PARAMETER = 2
- LENGTH = 3
- INVISIBLE = 4
-
-
-class VertexPlot(Enum):
- """What it is possible to show on a vertex."""
-
- BLACK = 1
- VERTEX_PARAMETER = 2
- INVISIBLE = 3
-
-
-def add_colorbar(fig: Figure, ax: Axes, v_min: float, v_max: float, cmap: Colormap) -> Colorbar:
- """Add a colorbar to the figure, with given min and max values and colormap."""
- fake_im = ax.imshow([[1]], vmin=v_min, vmax=v_max, cmap=cmap)
- colorbar = fig.colorbar(fake_im, ax=ax, shrink=0.7)
- fake_im.set_alpha(0)
- return colorbar
-
-
-def adjust_lightness(color: tuple[float, float, float], amount: float = 0.5) -> tuple[float, float, float]:
- """Adjust lightness of a color."""
- color = colorsys.rgb_to_hls(*to_rgb(color))
- return colorsys.hls_to_rgb(color[0], max(0, min(1, amount * color[1])), color[2])
-
-
-def get_cmap(n: int, name: str = "hsv") -> Callable[[int], tuple[float, float, float, float]]:
- """Returns a function that maps each index in 0, 1, ..., n-1 to a distinct RGB color.
-
- The keyword argument name must be a standard mpl colormap name.
- """
-
- def cmap_with_n_colors(i: int) -> tuple[float, float, float, float]:
- cmap = matplotlib.colormaps.get_cmap(name)
- return cmap(i / (n - 1))
-
- return cmap_with_n_colors
-
-
-def plot_mesh( # noqa: C901
- vertTable: Array,
- heTable: Array,
- faceTable: Array,
- width: float,
- height: float,
- flip_x: bool = False,
- flip_y: bool = False,
- multicolor: bool = True,
- lines: bool = True,
- vertices: bool = True,
- path: str = ".",
- name: str = "-1",
- save: bool = False,
- show: bool = True,
-) -> None:
- """General function to plot a mesh with periodic boundary conditions."""
- cmap = get_cmap(len(faceTable))
- all_verts = []
- for face in range(len(faceTable)):
- start_he = faceTable[face]
- he = start_he
-
- v_source = heTable[he][3]
- verts_sources = np.array([vertTable[v_source]])
- all_verts.append(vertTable[v_source])
-
- he_offset_x = heTable[he][6]
- he_offset_y = heTable[he][7]
- sum0_offsets = he_offset_x
- sum1_offsets = he_offset_y
-
- he = heTable[he][1]
-
- while he != start_he:
- v_source = heTable[he][3]
-
- all_verts.append(vertTable[v_source])
-
- verts_sources = np.concatenate(
- (
- verts_sources,
- (np.array([vertTable[v_source]]) + np.array([sum0_offsets * width, sum1_offsets * height])),
- ),
- axis=0,
- )
-
- he_offset_x = heTable[he][6]
- he_offset_y = heTable[he][7]
- sum0_offsets += he_offset_x
- sum1_offsets += he_offset_y
-
- he = heTable[he][1]
-
- v_source = heTable[he][3]
- verts_sources = np.concatenate((verts_sources, (np.array([vertTable[v_source]]))), axis=0)
- x, y = zip(*verts_sources, strict=False)
-
- if flip_x:
- x = tuple(np.array((width,) * len(x)) - x)
- if not flip_y:
- y = tuple(np.array((height,) * len(y)) - y)
-
- if multicolor:
- plt.fill(x, y, color=cmap(face), alpha=0.5)
- plt.fill(np.add(x, width), np.add(y, height), color=cmap(face), alpha=0.5)
- plt.fill(np.add(x, -width), np.add(y, -height), color=cmap(face), alpha=0.5)
- plt.fill(np.add(x, -width), np.add(y, height), color=cmap(face), alpha=0.5)
- plt.fill(np.add(x, width), np.add(y, -height), color=cmap(face), alpha=0.5)
- plt.fill(x, np.add(y, height), color=cmap(face), alpha=0.5)
- plt.fill(x, np.add(y, -height), color=cmap(face), alpha=0.5)
- plt.fill(np.add(x, width), y, color=cmap(face), alpha=0.5)
- plt.fill(np.add(x, -width), y, color=cmap(face), alpha=0.5)
-
- if lines:
- for i in range(0, len(x) - 1, 1):
- plt.plot(x[i : i + 2], y[i : i + 2], "-", color="black")
- plt.plot(
- tuple(np.add(x[i : i + 2], (width, width))),
- tuple(np.add(y[i : i + 2], (height, height))),
- "-",
- color="black",
- )
- plt.plot(
- tuple(np.add(x[i : i + 2], (-width, -width))),
- tuple(np.add(y[i : i + 2], (-height, -height))),
- "-",
- color="black",
- )
- plt.plot(
- tuple(np.add(x[i : i + 2], (-width, -width))),
- tuple(np.add(y[i : i + 2], (height, height))),
- "-",
- color="black",
- )
- plt.plot(
- tuple(np.add(x[i : i + 2], (width, width))),
- tuple(np.add(y[i : i + 2], (-height, -height))),
- "-",
- color="black",
- )
- plt.plot(x[i : i + 2], tuple(np.add(y[i : i + 2], (height, height))), "-", color="black")
- plt.plot(x[i : i + 2], tuple(np.add(y[i : i + 2], (-height, -height))), "-", color="black")
- plt.plot(tuple(np.add(x[i : i + 2], (width, width))), y[i : i + 2], "-", color="black")
- plt.plot(tuple(np.add(x[i : i + 2], (-width, -width))), y[i : i + 2], "-", color="black")
-
- if vertices:
- all_verts = np.array(all_verts)
- plt.scatter(all_verts[:, 0], height - all_verts[:, 1], color="black")
-
- plt.xlim([0, width])
- plt.ylim([0, height])
-
- plt.gca().set_aspect("equal")
-
- if save:
- Path(path).mkdir(exist_ok=True)
- # plt.savefig(path + str(name) + '.svg', format='svg')
- plt.savefig(path + str(name) + ".png", format="png")
-
- if show:
- plt.show()
-
- plt.clf()
-
-
-def plot_mesh_selected( # noqa: C901
- vertTable: Array,
- heTable: Array,
- faceTable: Array,
- selected_faces: Array,
- width: float,
- height: float,
- flip_x: bool = False,
- flip_y: bool = False,
- multicolor: bool = True,
- lines: bool = True,
- vertices: bool = True,
- path: str = ".",
- name: str = "-1",
- save: bool = False,
- show: bool = True,
-) -> None:
- """Plot only selected faces."""
- cmap = get_cmap(len(faceTable))
-
- all_verts = []
- for face in selected_faces:
- face = int(face)
-
- start_he = faceTable[face]
- he = start_he
-
- v_source = heTable[he][3]
-
- verts_sources = np.array([vertTable[v_source]])
- all_verts.append(vertTable[v_source])
-
- he_offset_x = heTable[he][6]
- he_offset_y = heTable[he][7]
- sum0_offsets = he_offset_x
- sum1_offsets = he_offset_y
-
- he = heTable[he][1]
-
- while he != start_he:
- v_source = heTable[he][3]
-
- all_verts.append(vertTable[v_source])
-
- verts_sources = np.concatenate(
- (
- verts_sources,
- (np.array([vertTable[v_source]]) + np.array([sum0_offsets * width, sum1_offsets * height])),
- ),
- axis=0,
- )
-
- he_offset_x = heTable[he][6]
- he_offset_y = heTable[he][7]
- sum0_offsets += he_offset_x
- sum1_offsets += he_offset_y
-
- he = heTable[he][1]
-
- v_source = heTable[he][3]
- verts_sources = np.concatenate((verts_sources, (np.array([vertTable[v_source]]))), axis=0)
-
- x, y = zip(*verts_sources, strict=False)
-
- if flip_x:
- x = tuple(np.array((width,) * len(x)) - x)
- if flip_y:
- y = tuple(np.array((height,) * len(y)) - y)
-
- if multicolor:
- plt.fill(x, y, color=cmap(face), alpha=0.5)
- plt.fill(np.add(x, width), np.add(y, height), color=cmap(face), alpha=0.5)
- plt.fill(np.add(x, -width), np.add(y, -height), color=cmap(face), alpha=0.5)
- plt.fill(np.add(x, -width), np.add(y, height), color=cmap(face), alpha=0.5)
- plt.fill(np.add(x, width), np.add(y, -height), color=cmap(face), alpha=0.5)
- plt.fill(x, np.add(y, height), color=cmap(face), alpha=0.5)
- plt.fill(x, np.add(y, -height), color=cmap(face), alpha=0.5)
- plt.fill(np.add(x, width), y, color=cmap(face), alpha=0.5)
- plt.fill(np.add(x, -width), y, color=cmap(face), alpha=0.5)
-
- if lines:
- for i in range(0, len(x) - 1, 1):
- plt.plot(x[i : i + 2], y[i : i + 2], "-", color="black")
- plt.plot(
- tuple(np.add(x[i : i + 2], (width, width))),
- tuple(np.add(y[i : i + 2], (height, height))),
- "-",
- color="black",
- )
- plt.plot(
- tuple(np.add(x[i : i + 2], (-width, -width))),
- tuple(np.add(y[i : i + 2], (-height, -height))),
- "-",
- color="black",
- )
- plt.plot(
- tuple(np.add(x[i : i + 2], (-width, -width))),
- tuple(np.add(y[i : i + 2], (height, height))),
- "-",
- color="black",
- )
- plt.plot(
- tuple(np.add(x[i : i + 2], (width, width))),
- tuple(np.add(y[i : i + 2], (-height, -height))),
- "-",
- color="black",
- )
- plt.plot(x[i : i + 2], tuple(np.add(y[i : i + 2], (height, height))), "-", color="black")
- plt.plot(x[i : i + 2], tuple(np.add(y[i : i + 2], (-height, -height))), "-", color="black")
- plt.plot(tuple(np.add(x[i : i + 2], (width, width))), y[i : i + 2], "-", color="black")
- plt.plot(tuple(np.add(x[i : i + 2], (-width, -width))), y[i : i + 2], "-", color="black")
-
- if vertices:
- x_all, y_all = zip(*all_verts, strict=False)
- plt.scatter(x_all, y_all, color="black")
-
- plt.xlim([0, width])
- plt.ylim([0, height])
-
- plt.gca().set_aspect("equal")
-
- if save:
- Path(path).mkdir(exist_ok=True)
- plt.savefig(path + str(name) + ".svg", format="svg")
- plt.savefig(path + str(name) + ".png", format="png")
-
- if show:
- plt.show()
-
- plt.clf()
-
-
-def plot_line_tens( # noqa: C901
- vertTable: Array,
- heTable: Array,
- faceTable: Array,
- file_txt: str,
- width: float,
- height: float,
- flip_x: bool = False,
- flip_y: bool = False,
- multicolor: bool = False,
- lines: bool = True,
- vertices: bool = False,
- path: str = ".",
- name: str = "-1",
- save: bool = False,
- show: bool = True,
-) -> None:
- """Plot mesh with line tensions."""
-
- def load_last_np_array(filename: str) -> NDArray:
- with Path(filename).open() as file:
- content = file.read().strip()
- # Split into separate arrays (assuming each starts with '[' and ends with ']')
- arrays = content.split("]\n[")
- last_array_text = arrays[-1].replace("[", "").replace("]", "")
- # Convert string to NumPy array
- last_array = np.fromstring(last_array_text, sep=" ")
- return last_array
-
- red_intensity = load_last_np_array(file_txt)
- print(red_intensity)
- red_intensity = (red_intensity - np.min(red_intensity)) / (np.max(red_intensity) - np.min(red_intensity))
-
- cmap = get_cmap(len(faceTable))
-
- all_verts = []
- for face in range(len(faceTable)):
- red_intensity_face = []
-
- start_he = faceTable[face]
- he = start_he
-
- red_intensity_face.append(red_intensity[he])
-
- v_source = heTable[he][3]
-
- verts_sources = np.array([vertTable[v_source]])
- all_verts.append(vertTable[v_source])
-
- he_offset_x = heTable[he][6]
- he_offset_y = heTable[he][7]
- sum0_offsets = he_offset_x
- sum1_offsets = he_offset_y
-
- he = heTable[he][1]
-
- while he != start_he:
- red_intensity_face.append(red_intensity[he])
-
- v_source = heTable[he][3]
-
- all_verts.append(vertTable[v_source])
-
- verts_sources = np.concatenate(
- (
- verts_sources,
- (np.array([vertTable[v_source]]) + np.array([sum0_offsets * width, sum1_offsets * height])),
- ),
- axis=0,
- )
-
- he_offset_x = heTable[he][6]
- he_offset_y = heTable[he][7]
- sum0_offsets += he_offset_x
- sum1_offsets += he_offset_y
-
- he = heTable[he][1]
-
- v_source = heTable[he][3]
- verts_sources = np.concatenate((verts_sources, (np.array([vertTable[v_source]]))), axis=0)
-
- x, y = zip(*verts_sources, strict=False)
-
- if flip_x:
- x = tuple(np.array((width,) * len(x)) - x)
- if flip_y:
- y = tuple(np.array((height,) * len(y)) - y)
-
- if multicolor:
- plt.fill(x, y, color=cmap(face), alpha=0.5)
- plt.fill(np.add(x, width), np.add(y, height), color=cmap(face), alpha=0.5)
- plt.fill(np.add(x, -width), np.add(y, -height), color=cmap(face), alpha=0.5)
- plt.fill(np.add(x, -width), np.add(y, height), color=cmap(face), alpha=0.5)
- plt.fill(np.add(x, width), np.add(y, -height), color=cmap(face), alpha=0.5)
- plt.fill(x, np.add(y, height), color=cmap(face), alpha=0.5)
- plt.fill(x, np.add(y, -height), color=cmap(face), alpha=0.5)
- plt.fill(np.add(x, width), y, color=cmap(face), alpha=0.5)
- plt.fill(np.add(x, -width), y, color=cmap(face), alpha=0.5)
-
- if lines:
- for i in range(0, len(x) - 1, 1):
- r = red_intensity_face[i] # Assumi che red_intensity sia una lista con valori tra 0 e 1
- color = (r, 0, 0) # (rosso, verde, blu)
- plt.plot(x[i : i + 2], y[i : i + 2], "-", color=color, lw=0.5 + r * 3.0)
- plt.plot(
- tuple(np.add(x[i : i + 2], (width, width))),
- tuple(np.add(y[i : i + 2], (height, height))),
- "-",
- color=color,
- lw=0.5 + r * 3.0,
- )
- plt.plot(
- tuple(np.add(x[i : i + 2], (-width, -width))),
- tuple(np.add(y[i : i + 2], (-height, -height))),
- "-",
- color=color,
- lw=0.5 + r * 3.0,
- )
- plt.plot(
- tuple(np.add(x[i : i + 2], (-width, -width))),
- tuple(np.add(y[i : i + 2], (height, height))),
- "-",
- color=color,
- lw=0.5 + r * 3.0,
- )
- plt.plot(
- tuple(np.add(x[i : i + 2], (width, width))),
- tuple(np.add(y[i : i + 2], (-height, -height))),
- "-",
- color=color,
- lw=0.5 + r * 3.0,
- )
- plt.plot(
- x[i : i + 2], tuple(np.add(y[i : i + 2], (height, height))), "-", color=color, lw=0.5 + r * 3.0
- )
- plt.plot(
- x[i : i + 2], tuple(np.add(y[i : i + 2], (-height, -height))), "-", color=color, lw=0.5 + r * 3.0
- )
- plt.plot(tuple(np.add(x[i : i + 2], (width, width))), y[i : i + 2], "-", color=color, lw=0.5 + r * 3.0)
- plt.plot(
- tuple(np.add(x[i : i + 2], (-width, -width))), y[i : i + 2], "-", color=color, lw=0.5 + r * 3.0
- )
-
- if vertices:
- x_all, y_all = zip(*all_verts, strict=False)
- plt.scatter(x_all, y_all, color="black")
-
- plt.xlim([0, width])
- plt.ylim([0, height])
-
- plt.gca().set_aspect("equal")
-
- if save:
- Path(path).mkdir(exist_ok=True)
- # plt.savefig(path + str(name) + '.svg', format='svg')
- plt.savefig(path + str(name) + ".png", format="png")
-
- if show:
- plt.show()
-
- plt.clf()
-
-
-# ==========
-# Bounded
-# ==========
-def draw_arc_N_get_points( # noqa: N802
- ang: Array, pos_source: Array, pos_target: Array, lines: bool, n: int = 100
-) -> tuple[NDArray, NDArray]:
- """Draw an arc of a circle with N points."""
- edge_vector = pos_target - pos_source
- edge_half_length = np.linalg.norm(edge_vector) / 2
- radius = edge_half_length / np.sin(ang)
- diameter = 2 * radius
- d_midpoint_to_center = np.sqrt(radius**2 - edge_half_length**2)
- midpoint = (pos_source + pos_target) / 2
- unit_vector = edge_vector / np.linalg.norm(edge_vector)
- unit_vector_midpoint_to_center = np.array([-unit_vector[1], unit_vector[0]])
- center = midpoint + unit_vector_midpoint_to_center * d_midpoint_to_center
- ang_source = np.angle(np.dot((pos_source - center), np.array([1, 1j])))
- ang_target = np.angle(np.dot((pos_target - center), np.array([1, 1j])))
- rad2deg = 180 / np.pi
- normalized_ang_source_degrees = ang_source * rad2deg
- normalized_ang_target_degrees = ang_target * rad2deg
- if normalized_ang_source_degrees < 0.0:
- normalized_ang_source_degrees += 360
- if normalized_ang_target_degrees < 0.0:
- normalized_ang_target_degrees += 360
- if lines:
- surface_arc = Arc(
- center,
- diameter,
- diameter,
- theta1=normalized_ang_source_degrees,
- theta2=normalized_ang_target_degrees,
- color="black",
- linewidth=2.0,
- )
- plt.gca().add_patch(surface_arc)
- tau = 2 * np.pi
- if abs(ang_target - ang_source) > np.pi:
- if ang_source < ang_target:
- ang_source += tau
- else:
- ang_target += tau
- intermediate_angles = np.linspace(ang_source, ang_target, n, False)[1:]
- points = [pos_source]
- points.extend([radius * np.array([np.cos(a), np.sin(a)]) + center for a in intermediate_angles])
- points.append(pos_target)
- points.append(pos_source)
- x, y = zip(*points, strict=False)
- return x, y
-
-
-def plot_bounded_mesh( # noqa: C901
- vertTable: NDArray,
- angTable: NDArray,
- heTable: NDArray,
- faceTable: NDArray,
- L_box: float,
- flip_x: bool = False,
- flip_y: bool = False,
- multicolor: bool = True,
- lines: bool = True,
- vertices: bool = False,
- fates: bool = False,
- path: str = ".",
- name: str = "-1",
- save: bool = False,
- show: bool = True,
-) -> None:
- """Plot a bounded mesh."""
- cmap = get_cmap(np.max(faceTable[:, 1]) + 1, name="viridis") if fates else get_cmap(faceTable.shape[0])
- draw_curve_threshold = 0.01 # radians. Must be above 0 to avoid overcomplicating a simple plot
-
- all_verts = []
- for face in range(faceTable.shape[0]):
- surfaces = []
- is_surface = False
- start_he = int(faceTable[face, 0])
- he = start_he
- v_source = int(heTable[he][3] + heTable[he][5])
- pos_source = vertTable[v_source - 2]
- if heTable[he][3] == 0:
- ang = angTable[he // 2]
- if ang > draw_curve_threshold:
- is_surface = True
- v_target = int(heTable[he][4] + heTable[he][6] - 1)
- pos_target = vertTable[v_target - 2]
- arcx, arcy = draw_arc_N_get_points(ang, pos_source, pos_target, lines)
- if multicolor:
- if fates:
- plt.fill(arcx, arcy, facecolor=cmap(faceTable[face, 1]), alpha=0.5)
- else:
- plt.fill(arcx, arcy, facecolor=cmap(face), alpha=0.5)
- verts_sources = np.array([pos_source])
- all_verts.append(pos_source)
- surfaces.append(is_surface)
- he = int(heTable[he][1])
-
- while he != start_he:
- is_surface = False
- v_source = int(heTable[he][3] + heTable[he][5])
- pos_source = vertTable[v_source - 2]
- if heTable[he][3] == 0:
- ang = angTable[he // 2]
- if ang > draw_curve_threshold:
- is_surface = True
- v_target = int(heTable[he][4] + heTable[he][6] - 1)
- pos_target = vertTable[v_target - 2]
- arcx, arcy = draw_arc_N_get_points(ang, pos_source, pos_target, lines)
- if multicolor:
- if fates:
- plt.fill(arcx, arcy, facecolor=cmap(faceTable[face, 1]), alpha=0.5)
- else:
- plt.fill(arcx, arcy, facecolor=cmap(face), alpha=0.5)
- all_verts.append(pos_source)
- verts_sources = np.concatenate([verts_sources, pos_source[None]], axis=0)
- surfaces.append(is_surface)
- he = int(heTable[he][1])
-
- v_source = int(heTable[he][3] + heTable[he][5])
- pos_source = vertTable[v_source - 2]
- verts_sources = np.concatenate([verts_sources, pos_source[None]], axis=0)
-
- x, y = zip(*verts_sources, strict=False)
-
- if flip_x:
- x = tuple(np.array((L_box,) * len(x)) - x)
- if flip_y:
- y = tuple(np.array((L_box,) * len(y)) - y)
-
- if multicolor:
- if fates:
- plt.fill(x, y, facecolor=cmap(faceTable[face, 1]), alpha=0.5)
- else:
- plt.fill(x, y, facecolor=cmap(face), alpha=0.5)
-
- if lines:
- for i in range(0, len(x) - 1, 1):
- if not surfaces[i]:
- plt.plot(x[i : i + 2], y[i : i + 2], "-", color="black")
-
- if vertices:
- x_all, y_all = zip(*all_verts, strict=False)
- plt.scatter(x_all, y_all, color="black")
-
- # unlike the pbc case, here is not easy to know a priori what the limits of the progressively optimized cell cluster
- # will be (across a stack of images)
- plt.xlim([-1.5, L_box + 0.5])
- plt.ylim([-1.5, L_box + 0.5])
- plt.gca().set_aspect("equal")
-
- if save:
- Path(path).mkdir(exist_ok=True)
- plt.savefig(path + str(name) + ".pdf") # format maybe should be left as a choice
-
- if show:
- plt.show()
-
- plt.clf()
diff --git a/src/vertax/start.py b/src/vertax/start.py
deleted file mode 100644
index 840480c..0000000
--- a/src/vertax/start.py
+++ /dev/null
@@ -1,796 +0,0 @@
-"""Module for mesh creation, from given seeds or an image."""
-
-from __future__ import annotations
-
-from pathlib import Path
-from typing import TYPE_CHECKING
-
-import jax.numpy as jnp
-import numpy as np
-from scipy.spatial import Voronoi
-
-from vertax.mask_analysis import find_vertices_edges_faces, mask_from_image, pad
-from vertax.plot import plot_bounded_mesh
-
-if TYPE_CHECKING:
- from jax import Array
- from matplotlib.pylab import Generator
- from numpy.typing import NDArray
-
-
-def load_mesh(path: str) -> tuple[Array, Array, Array]:
- """Load a mesh from a file.
-
- Args:
- path (str): Path to the mesh file.
-
- Returns:
- tuple[Array, Array, Array]: Jax arrays of vertices (2D, float), edges and faces.
- """
- mesh_file = np.load(path)
- return (jnp.array(mesh_file["vertices"]), jnp.array(mesh_file["edges"]), jnp.array(mesh_file["faces"]))
- # return jnp.load(path + "vertTable.npy"), jnp.load(path + "heTable.npy"), jnp.load(path + "faceTable.npy")
-
-
-def save_mesh(path: str, vertTable: Array, heTable: Array, faceTable: Array) -> None:
- """Save a mesh to a file.
-
- Args:
- path (str): Path to the saved file.
- vertTable (Array): The vertices of the mesh.
- heTable (Array): The half-edges of the mesh.
- faceTable (Array): The faces of the mesh.
- """
- Path(path).parent.mkdir(parents=True, exist_ok=True)
- np.savez_compressed(path, allow_pickle=False, vertices=vertTable, edges=heTable, faces=faceTable)
-
-
-def create_mesh_from_seeds(seeds: Array) -> tuple[Array, Array, Array]: # noqa: C901
- """Create a Periodic Voronoi Mesh from a list of seeds.
-
- The result is given in a Half-Edge structure.
-
- Args:
- seeds (Array): 2D float jax array of seed positions.
-
- Returns:
- tuple[Array, Array, Array]: The vertices, half-edges and faces table of the mesh.
- """
- n_cells = len(seeds)
- L_box = np.sqrt(n_cells)
-
- # PERIODIC VORONOI - VERTICES EDGES FACES
-
- if n_cells < 20:
- print("\nWarning: [n_cells < 20] initial condition may not work as expected.\n")
-
- padded_seeds = np.concatenate(
- (
- seeds,
- np.add(seeds, np.full((n_cells, 2), [-L_box, +L_box])),
- np.add(seeds, np.full((n_cells, 2), [0, +L_box])),
- np.add(seeds, np.full((n_cells, 2), [L_box, +L_box])),
- np.add(seeds, np.full((n_cells, 2), [-L_box, 0])),
- np.add(seeds, np.full((n_cells, 2), [L_box, 0])),
- np.add(seeds, np.full((n_cells, 2), [-L_box, -L_box])),
- np.add(seeds, np.full((n_cells, 2), [0, -L_box])),
- np.add(seeds, np.full((n_cells, 2), [L_box, -L_box])),
- ),
- axis=0,
- )
-
- voronoi = Voronoi(padded_seeds)
-
- vertices = voronoi.vertices
- edges = voronoi.ridge_vertices
- faces = voronoi.regions
-
- col0_mask = (vertices[:, 0] >= 0.0) & (vertices[:, 0] <= L_box)
- col1_mask = (vertices[:, 1] >= 0.0) & (vertices[:, 1] <= L_box)
-
- periodic_voronoi_vertices_idx = np.arange(len(vertices))[col0_mask & col1_mask]
- periodic_voronoi_vertices_pos = vertices[col0_mask & col1_mask]
-
- edges_inside = []
- edges_outside = []
- offsets_inside = {}
- offsets_outside = {}
- visited = []
-
- for e in edges:
- if e[0] in periodic_voronoi_vertices_idx and e[1] in periodic_voronoi_vertices_idx:
- edges_inside.append(tuple(sorted((e[0], e[1]))))
- offsets_inside[(e[0], e[1])] = (0, 0)
- offsets_inside[(e[1], e[0])] = (0, 0)
- if bool(e[0] in periodic_voronoi_vertices_idx) != bool(e[1] in periodic_voronoi_vertices_idx):
- if e[0] in periodic_voronoi_vertices_idx:
- if vertices[e[1]][0] < 0.0:
- x = vertices[e[1]][0] + L_box
- offset_x1 = -1
- elif vertices[e[1]][0] > L_box:
- x = vertices[e[1]][0] - L_box
- offset_x1 = 1
- else:
- x = vertices[e[1]][0]
- offset_x1 = 0
- if vertices[e[1]][1] < 0.0:
- y = vertices[e[1]][1] + L_box
- offset_y1 = -1
- elif vertices[e[1]][1] > L_box:
- y = vertices[e[1]][1] - L_box
- offset_y1 = 1
- else:
- y = vertices[e[1]][1]
- offset_y1 = 0
- for idx, pos in zip(periodic_voronoi_vertices_idx, periodic_voronoi_vertices_pos, strict=False):
- if ((np.abs(pos[0] - x)) < 10**-8) and ((np.abs(pos[1] - y)) < 10**-8):
- edges_outside.append(tuple(sorted((e[0], idx))))
- if (e[0], e[1]) not in visited and (e[1], e[0]) not in visited:
- offsets_outside[(e[0], idx)] = (offset_x1, offset_y1)
- offsets_outside[(idx, e[0])] = (-offset_x1, -offset_y1)
- visited.append((e[0], e[1]))
- visited.append((e[1], e[0]))
- break
- else:
- if vertices[e[0]][0] < 0.0:
- x = vertices[e[0]][0] + L_box
- offset_x0 = -1
- elif vertices[e[0]][0] > L_box:
- x = vertices[e[0]][0] - L_box
- offset_x0 = 1
- else:
- x = vertices[e[0]][0]
- offset_x0 = 0
- if vertices[e[0]][1] < 0.0:
- y = vertices[e[0]][1] + L_box
- offset_y0 = -1
- elif vertices[e[0]][1] > L_box:
- y = vertices[e[0]][1] - L_box
- offset_y0 = 1
- else:
- y = vertices[e[0]][1]
- offset_y0 = 0
- for idx, pos in zip(periodic_voronoi_vertices_idx, periodic_voronoi_vertices_pos, strict=False):
- if ((np.abs(pos[0] - x)) < 10**-8) and ((np.abs(pos[1] - y)) < 10**-8):
- edges_outside.append(tuple(sorted((idx, e[1]))))
- if (e[0], e[1]) not in visited and (e[1], e[0]) not in visited:
- offsets_outside[(idx, e[1])] = (-offset_x0, -offset_y0)
- offsets_outside[(e[1], idx)] = (offset_x0, offset_y0)
- visited.append((e[0], e[1]))
- visited.append((e[1], e[0]))
- break
-
- periodic_voronoi_edges = list(set(edges_inside)) + list(set(edges_outside))
-
- offsets = offsets_inside | offsets_outside
-
- faces_inside = []
- faces_inside_outside = []
- for face in faces:
- if face:
- if all(item in periodic_voronoi_vertices_idx for item in face):
- faces_inside.append(tuple(sorted(face)))
- if any(item in face for item in periodic_voronoi_vertices_idx):
- face_inside_outside = []
- for f in face:
- if f in periodic_voronoi_vertices_idx:
- face_inside_outside.append(f)
- else:
- if vertices[f][0] < 0.0:
- x = vertices[f][0] + L_box
- elif vertices[f][0] > L_box:
- x = vertices[f][0] - L_box
- else:
- x = vertices[f][0]
- if vertices[f][1] < 0.0:
- y = vertices[f][1] + L_box
- elif vertices[f][1] > L_box:
- y = vertices[f][1] - L_box
- else:
- y = vertices[f][1]
- for idx, pos in zip(periodic_voronoi_vertices_idx, periodic_voronoi_vertices_pos, strict=False):
- if ((np.abs(pos[0] - x)) < 10**-8) and ((np.abs(pos[1] - y)) < 10**-8):
- face_inside_outside.append(idx)
- break
-
- faces_inside_outside.append(tuple(sorted(face_inside_outside)))
-
- periodic_voronoi_faces = list(set(faces_inside_outside))
-
- # HALF EDGE DATA STRUCTURE
-
- # Reciprocating edges
- periodic_voronoi_half_edges = []
- for e in periodic_voronoi_edges:
- periodic_voronoi_half_edges.append(e)
- periodic_voronoi_half_edges.append((e[1], e[0]))
-
- # Finding clockwise (or counterclockwise) half edge set for each face
- ordered_edges_periodic_voronoi_faces = []
- for face in periodic_voronoi_faces:
- edges_face = [(f1, f2) for f1 in face for f2 in face if (f1, f2) in periodic_voronoi_edges]
- i = 0
- start_edge = edges_face[i]
- ordered_face = [start_edge]
- e = start_edge
- visited = [e]
- while sorted(edges_face) != sorted(visited):
- if e[0] == start_edge[1] and e not in visited:
- ordered_face.append(e)
- start_edge = e
- visited.append(e)
- if e[1] == start_edge[1] and e not in visited:
- ordered_face.append((e[1], e[0]))
- start_edge = (e[1], e[0])
- visited.append(e)
- i += 1
- e = edges_face[i % len(edges_face)]
-
- order = 0
- sum0_offsets = 0
- sum1_offsets = 0
- points = []
- for e in ordered_face:
- idx0 = list(periodic_voronoi_vertices_idx).index(e[0])
- idx1 = list(periodic_voronoi_vertices_idx).index(e[1])
- e_offsets = offsets[e]
-
- prev_sum0_offsets = sum0_offsets
- prev_sum1_offsets = sum1_offsets
- sum0_offsets += e_offsets[0]
- sum1_offsets += e_offsets[1]
-
- order += (
- (periodic_voronoi_vertices_pos[idx1][0] + sum0_offsets * L_box)
- - (periodic_voronoi_vertices_pos[idx0][0] + prev_sum0_offsets * L_box)
- ) * (
- (periodic_voronoi_vertices_pos[idx1][1] + sum1_offsets * L_box)
- + (periodic_voronoi_vertices_pos[idx0][1] + prev_sum1_offsets * L_box)
- )
-
- points.append(
- (
- periodic_voronoi_vertices_pos[idx0][0] + prev_sum0_offsets * L_box,
- periodic_voronoi_vertices_pos[idx0][1] + prev_sum1_offsets * L_box,
- )
- )
-
- points.append(
- (
- periodic_voronoi_vertices_pos[idx1][0] + sum0_offsets * L_box,
- periodic_voronoi_vertices_pos[idx1][1] + sum1_offsets * L_box,
- )
- )
-
- if order < 0:
- ordered_edges_periodic_voronoi_faces.append(ordered_face)
- if order > 0:
- new_ordered_face = [(e[1], e[0]) for e in reversed(ordered_face)]
- ordered_edges_periodic_voronoi_faces.append(new_ordered_face)
- if order == 0:
- print("\nError: no order detected for face " + str(face) + "\n")
- exit()
-
- # VERT FACE HE TABLES
-
- vertTable = np.zeros((len(periodic_voronoi_vertices_idx), 2))
- for i, (_, pos) in enumerate(zip(periodic_voronoi_vertices_idx, periodic_voronoi_vertices_pos, strict=False)):
- vertTable[i][0] = pos[0] # x pos vert
- vertTable[i][1] = pos[1] # y pos vert
- # vertTable[i][2] = idx_selected_he # he vert source (random among three)
-
- faceTable = np.zeros(len(periodic_voronoi_faces))
- for i, hedges_face in enumerate(ordered_edges_periodic_voronoi_faces):
- for j, he in enumerate(periodic_voronoi_half_edges):
- if he == hedges_face[0]:
- faceTable[i] = j # he_inside
-
- heTable = np.zeros((len(periodic_voronoi_half_edges), 8))
- for i, he in enumerate(periodic_voronoi_half_edges):
- for hedges_face in ordered_edges_periodic_voronoi_faces:
- if he in hedges_face:
- idx = hedges_face.index(he)
- heTable[i][0] = periodic_voronoi_half_edges.index(hedges_face[(idx - 1) % len(hedges_face)]) # he_prev
- heTable[i][1] = periodic_voronoi_half_edges.index(hedges_face[(idx + 1) % len(hedges_face)]) # he_next
- heTable[i][3] = list(periodic_voronoi_vertices_idx).index(he[0]) # vert source
- heTable[i][4] = list(periodic_voronoi_vertices_idx).index(he[1]) # vert target
- heTable[i][5] = ordered_edges_periodic_voronoi_faces.index(hedges_face) # face
- break
- heTable[i][2] = periodic_voronoi_half_edges.index((he[1], he[0])) # he twin
- heTable[i][6] = offsets[he][0] # he_offset x vert target
- heTable[i][7] = offsets[he][1] # he_offset y vert target
-
- return (
- jnp.array(vertTable, dtype=np.float32),
- jnp.array(heTable, dtype=np.int32),
- jnp.array(faceTable, dtype=np.int32),
- )
-
-
-def create_mesh_from_image(image: NDArray) -> tuple[Array, Array, Array]:
- """Create a rudimentary mesh with periodic boundary conditions from an image.
-
- To do that, we perform a segmentation using Cellpose and we try to fill the holes.
- The result will probably be imperfect and it will always be better if you
- provide directly a mask (with no holes) with the function "create_mesh_from_mask".
-
- Args:
- image (NDArray): The image which will act as a template for the mesh.
-
- Returns:
- tuple[Array, Array, Array]: The vertices, half-edges and faces table of the mesh.
- """
- return create_mesh_from_mask(mask_from_image(image))
-
-
-def create_mesh_from_mask(mask: NDArray) -> tuple[Array, Array, Array]: # noqa: C901
- """Create a rudimentary mesh with periodic boundary conditions from a mask with no holes.
-
- Args:
- mask (NDArray): The mask with no holes which will act as a template for the mesh.
-
- Returns:
- tuple[Array, Array, Array]: The vertices, half-edges and faces table of the mesh.
- """
- padded_mask = pad(mask, save=False, output_path="refined_and_padded_image.tiff")
- # Find vertices, edges, faces
- vertices, edges, faces = find_vertices_edges_faces(padded_mask)
-
- # imread tiff = Y is the first axis, X the second.
- height: int = mask.shape[0] # original image length. Padded is 3 times bigger.
- y_min = height / 2
- y_max = 2 * height + (height / 2)
- width: int = mask.shape[1]
- x_min = width / 2
- x_max = 2 * width + (width / 2)
-
- col0_mask = (vertices[:, 0] >= x_min) & (vertices[:, 0] < x_max)
- col1_mask = (vertices[:, 1] >= y_min) & (vertices[:, 1] < y_max)
-
- periodic_vertices_idx = np.arange(len(vertices))[col0_mask & col1_mask]
- periodic_vertices_pos = vertices[col0_mask & col1_mask]
-
- # store map between vertex id -> inside vertex id
- inside_vertex: dict[int, int] = {idx: idx for idx in periodic_vertices_idx}
- for i, vertex in enumerate(vertices):
- if i not in periodic_vertices_idx:
- x, y = vertex
- if x < x_min:
- x += 2 * width
- elif x >= x_max:
- x -= 2 * width
-
- if y < y_min:
- y += 2 * height
- elif y >= y_max:
- y -= 2 * height
-
- # Find corresponding inside vertex to the outside dest vertex
- for idx, pos in zip(periodic_vertices_idx, periodic_vertices_pos, strict=True):
- if np.max(np.abs(pos - [x, y])) < 1:
- inside_vertex[i] = idx
- break
-
- edges_inside = []
- edges_outside = []
- offsets_inside = {}
- offsets_outside = {}
- visited = []
-
- for e in edges:
- if e[0] in periodic_vertices_idx and e[1] in periodic_vertices_idx:
- edges_inside.append(tuple(sorted((e[0], e[1]))))
- offsets_inside[(e[0], e[1])] = (0, 0)
- offsets_inside[(e[1], e[0])] = (0, 0)
- elif bool(e[0] in periodic_vertices_idx) != bool(e[1] in periodic_vertices_idx):
- if e[0] in periodic_vertices_idx:
- # origin in, dest out
- # check x coord
- if vertices[e[1]][0] < x_min:
- offset_x1 = -1
- elif vertices[e[1]][0] >= x_max:
- offset_x1 = 1
- else:
- offset_x1 = 0
-
- # Now check y coord
- if vertices[e[1]][1] < y_min:
- offset_y1 = -1
- elif vertices[e[1]][1] >= y_max:
- offset_y1 = 1
- else:
- offset_y1 = 0
-
- # Find corresponding inside vertex to the outside dest vertex
- if e[1] not in inside_vertex:
- print(f"Error, no inside vertex found for vertex {e[1]}.")
- else:
- idx = inside_vertex[e[1]]
- edges_outside.append(tuple(sorted((e[0], idx))))
- if (e[0], e[1]) not in visited and (e[1], e[0]) not in visited:
- offsets_outside[(e[0], idx)] = (offset_x1, offset_y1)
- offsets_outside[(idx, e[0])] = (-offset_x1, -offset_y1)
- visited.append((e[0], e[1]))
- visited.append((e[1], e[0]))
- else:
- # dest in, origin out
- if vertices[e[0]][0] < x_min:
- offset_x0 = -1
- elif vertices[e[0]][0] >= x_max:
- offset_x0 = 1
- else:
- offset_x0 = 0
-
- if vertices[e[0]][1] < y_min:
- offset_y0 = -1
- elif vertices[e[0]][1] >= y_max:
- offset_y0 = 1
- else:
- offset_y0 = 0
-
- # Find corresponding inside vertex to the outside dest vertex
- if e[0] not in inside_vertex:
- print(f"Error, no inside vertex found for vertex {e[0]}.")
- else:
- idx = inside_vertex[e[0]]
- edges_outside.append(tuple(sorted((idx, e[1]))))
- if (e[0], e[1]) not in visited and (e[1], e[0]) not in visited:
- offsets_outside[(idx, e[1])] = (-offset_x0, -offset_y0)
- offsets_outside[(e[1], idx)] = (offset_x0, offset_y0)
- visited.append((e[0], e[1]))
- visited.append((e[1], e[0]))
-
- periodic_edges = list(set(edges_inside)) + list(set(edges_outside))
- offsets = offsets_inside | offsets_outside
-
- periodic_faces: list[set[int]] = [
- {inside_vertex[i] for i in face} for face in faces if any(v_id in periodic_vertices_idx for v_id in face)
- ]
-
- # HALF EDGE DATA STRUCTURE
-
- # Reciprocating edges
- periodic_half_edges = []
- for e in periodic_edges:
- periodic_half_edges.append(e)
- periodic_half_edges.append((e[1], e[0]))
-
- # Finding clockwise (or counterclockwise) half edge set for each face
- ordered_edges_periodic_faces = []
- for k, face in enumerate(periodic_faces):
- edges_face = [(f1, f2) for f1 in face for f2 in face if (f1, f2) in periodic_edges]
- i = 0
- start_edge = edges_face[0]
- ordered_face = [start_edge]
- e = start_edge
- visited = [e]
-
- while sorted(edges_face) != sorted(visited):
- if e[0] == start_edge[1] and e not in visited:
- ordered_face.append(e)
- start_edge = e
- visited.append(e)
- if e[1] == start_edge[1] and e not in visited:
- ordered_face.append((e[1], e[0]))
- start_edge = (e[1], e[0])
- visited.append(e)
- i += 1
- e = edges_face[i % len(edges_face)]
- order = 0
- sum0_offsets = 0
- sum1_offsets = 0
- points = []
- for e in ordered_face:
- idx0 = list(periodic_vertices_idx).index(e[0])
- idx1 = list(periodic_vertices_idx).index(e[1])
- e_offsets = offsets[e]
- prev_sum0_offsets = sum0_offsets
- prev_sum1_offsets = sum1_offsets
- sum0_offsets += e_offsets[0]
- sum1_offsets += e_offsets[1]
- order += (
- (periodic_vertices_pos[idx1][0] + sum0_offsets * (2 * width))
- - (periodic_vertices_pos[idx0][0] + prev_sum0_offsets * (2 * width))
- ) * (
- (periodic_vertices_pos[idx1][1] + sum1_offsets * (2 * height))
- + (periodic_vertices_pos[idx0][1] + prev_sum1_offsets * (2 * height))
- )
- points.append(
- (
- periodic_vertices_pos[idx0][0] + prev_sum0_offsets * (2 * width),
- periodic_vertices_pos[idx0][1] + prev_sum1_offsets * (2 * height),
- )
- )
- points.append(
- (
- periodic_vertices_pos[idx1][0] + sum0_offsets * (2 * width),
- periodic_vertices_pos[idx1][1] + sum1_offsets * (2 * height),
- )
- )
- if order < 0:
- ordered_edges_periodic_faces.append(ordered_face)
- if order > 0:
- new_ordered_face = [(e[1], e[0]) for e in reversed(ordered_face)]
- ordered_edges_periodic_faces.append(new_ordered_face)
- if order == 0:
- print(f"f\n{k} face Error: no order detected for face " + str(face) + "\n")
- print(vertices[np.array(face)])
- exit()
-
- # VERT FACE HE TABLES
- vertTable = periodic_vertices_pos - [x_min, y_min]
-
- faceTable = np.zeros(len(periodic_faces))
- for i, hedges_face in enumerate(ordered_edges_periodic_faces):
- for j, he in enumerate(periodic_half_edges):
- if he == hedges_face[0]:
- faceTable[i] = j # he_inside
-
- heTable = np.zeros((len(periodic_half_edges), 8))
- for i, he in enumerate(periodic_half_edges):
- for hedges_face in ordered_edges_periodic_faces:
- if he in hedges_face:
- idx = hedges_face.index(he)
- heTable[i][0] = periodic_half_edges.index(hedges_face[(idx - 1) % len(hedges_face)]) # he_prev
- heTable[i][1] = periodic_half_edges.index(hedges_face[(idx + 1) % len(hedges_face)]) # he_next
- heTable[i][3] = list(periodic_vertices_idx).index(he[0]) # vert source
- heTable[i][4] = list(periodic_vertices_idx).index(he[1]) # vert target
- heTable[i][5] = ordered_edges_periodic_faces.index(hedges_face) # face
- break
- heTable[i][2] = periodic_half_edges.index((he[1], he[0])) # he twin
- heTable[i][6] = offsets[he][0] # he_offset x vert target
- heTable[i][7] = offsets[he][1] # he_offset y vert target
-
- return (
- jnp.array(vertTable, dtype=np.float32),
- jnp.array(heTable, dtype=np.int32),
- jnp.array(faceTable, dtype=np.int32),
- )
-
-
-# ==========
-# Bounded
-# ==========
-
-
-def load_bounded_mesh(path: str) -> tuple[Array, Array, Array, Array]:
- """Load a mesh from a file.
-
- Args:
- path (str): Path to the mesh file.
-
- Returns:
- tuple[Array, Array, Array]: Jax arrays of vertices (2D, float), edges and faces.
- """
- mesh_file = np.load(path)
- return (
- jnp.array(mesh_file["vertices"]),
- jnp.array(mesh_file["angles"]),
- jnp.array(mesh_file["edges"]),
- jnp.array(mesh_file["faces"]),
- )
-
-
-def save_bounded_mesh(path: str, vertTable: Array, angTable: Array, heTable: Array, faceTable: Array) -> None:
- """Save a bounded mesh to a file.
-
- Args:
- path (str): Path to the saved file.
- vertTable (Array): The vertices of the mesh.
- angTable (Array): The angles of the mesh.
- heTable (Array): The half-edges of the mesh.
- faceTable (Array): The faces of the mesh.
- """
- Path(path).parent.mkdir(parents=True, exist_ok=True)
- np.savez_compressed(path, allow_pickle=False, vertices=vertTable, angles=angTable, edges=heTable, faces=faceTable)
-
-
-def create_bounded_mesh_from_seeds( # noqa: C901
- seeds: Array, path: str = "./", show: bool = False, rng: Generator | None = None
-) -> tuple[Array, Array, Array, Array]: # rng=numpy's random number generator
- """Create a bounded Mesh from a list of seeds.
-
- The seeds are assumed to have positive x and y positions.
-
- Args:
- seeds (Array[float32]): jax float array of seed positions of shape (nbSeeds, 2).
- path (str): where to save the mesh data.
- show (bool): whether to plot the mesh or not.
- rng (Generator|None): random number generator to add new seeds if needed, decide on cell fates...
- """
- n_cells = len(seeds) # starting number of seeds must be equal to the desired number of cells (faces)
- L_box = np.sqrt(n_cells)
- if rng is None:
- rng = np.random.default_rng()
-
- while True:
- success = 0
- voronoi = Voronoi(seeds)
-
- vertices = voronoi.vertices
- edges = voronoi.ridge_vertices
- faces = voronoi.regions
-
- inbound_faces = []
- inbound_vertices = np.zeros(vertices.shape[0], dtype=np.int32)
- for face in faces:
- if face and all(item > -1 for item in face): # the face must not be an empty list
- face_vertices_positions = vertices[face]
- if np.all(face_vertices_positions < L_box) and np.all(face_vertices_positions > 0):
- inbound_faces.append(face)
- inbound_vertices[face] += 1
-
- # getting rid of faces connected to a single other inbound face
- # (these can be problematic and lead to many special cases later on)
- while True:
- num_infaces = len(inbound_faces)
- del_count = 0
- for i, face in enumerate(reversed(inbound_faces)):
- if np.sum(inbound_vertices[face] > 1) <= 2:
- inbound_vertices[face] -= 1
- del inbound_faces[num_infaces - i - 1]
- del_count += 1
- if del_count == 0:
- break
-
- if num_infaces < n_cells:
- success = 1
- elif num_infaces > n_cells:
- success = 2
- else:
- for i, face in enumerate(inbound_faces):
- useful_vertices = []
- extra_edges = []
- last_useful = -1
- incomplete_new_edge = False
- new_edge = [] # now there
- for vertex in face:
- if inbound_vertices[vertex] == 1:
- if not incomplete_new_edge:
- # new_edge = [] # previously here
- new_edge.append(last_useful)
- incomplete_new_edge = True
- else:
- useful_vertices.append(vertex)
- last_useful = vertex
- if incomplete_new_edge:
- new_edge.append(vertex)
- extra_edges.append(new_edge)
- incomplete_new_edge = False
- if extra_edges and extra_edges[0][0] == -1:
- extra_edges[0][0] = useful_vertices[-1]
- elif incomplete_new_edge:
- new_edge.append(useful_vertices[0])
- extra_edges.append(new_edge)
- edges.extend(extra_edges)
- inbound_faces[i] = tuple(sorted(useful_vertices))
- useful_vertices_set = set(np.where(inbound_vertices > 1)[0])
-
- # HALF EDGE DATA STRUCTURE
-
- # reciprocating edges
-
- useful_edges = [tuple(sorted(e)) for e in edges if set(e).issubset(useful_vertices_set)]
-
- # failing to abide by the following relation results in disconnected topologies
- if len(useful_edges) != (n_cells - 1) * 3:
- success = 2
- else:
- half_edges = []
- for e in useful_edges:
- half_edges.append(e)
- half_edges.append((e[1], e[0]))
-
- # finding clockwise (or counterclockwise) half edge set for each face
-
- ordered_edges_inbound_faces = []
- for face in inbound_faces:
- ### I think scipy give you everything already ordered for finite faces
- edges_face = [(f1, f2) for f1 in face for f2 in face if (f1, f2) in useful_edges]
-
- i = 0
- start_edge = edges_face[i]
- ordered_face = [start_edge]
- e = start_edge
- visited = [e]
- while sorted(edges_face) != sorted(visited):
- if e[0] == start_edge[1] and e not in visited:
- ordered_face.append(e)
- start_edge = e
- visited.append(e)
- if e[1] == start_edge[1] and e not in visited:
- ordered_face.append((e[1], e[0]))
- start_edge = (e[1], e[0])
- visited.append(e)
- i += 1
- e = edges_face[i % len(edges_face)]
-
- order = 0
- for e in ordered_face:
- idx0 = e[0]
- idx1 = e[1]
-
- order += (vertices[idx1][0] - vertices[idx0][0]) * (vertices[idx1][1] + vertices[idx0][1])
-
- if order < 0:
- ordered_edges_inbound_faces.append(ordered_face)
- if order > 0:
- ordered_edges_inbound_faces.append([(e[1], e[0]) for e in reversed(ordered_face)])
- if order == 0:
- print("\nError: no order detected for face " + str(face) + "\n")
- exit()
-
- useful_vertices_list = list(useful_vertices_set)
- vertTable = np.zeros((len(useful_vertices_list), 2))
- for i, idx in enumerate(useful_vertices_list):
- pos = vertices[idx]
- vertTable[i][0] = pos[0] # x pos vert
- vertTable[i][1] = pos[1] # y pos vert
-
- faceTable = np.zeros((len(inbound_faces), 1), dtype=np.int32)
- for i, hedges_face in enumerate(ordered_edges_inbound_faces):
- for j, he in enumerate(half_edges):
- if he == hedges_face[0]:
- faceTable[i] = j # he_inside
- faceTable = _fate_selection(faceTable, 2, rng)
-
- L_he = len(half_edges)
- heTable = np.zeros((L_he, 8), dtype=np.int32)
- heTable[:, 4] = 1
- heTable[:, 6] = 1
- relevant_twins = []
- for i, he in enumerate(half_edges):
- belongs_to_any_face = False
- for hedges_face in ordered_edges_inbound_faces:
- if he in hedges_face:
- idx = hedges_face.index(he)
- heTable[i][0] = half_edges.index(hedges_face[(idx - 1) % len(hedges_face)]) # he_prev
- heTable[i][1] = half_edges.index(hedges_face[(idx + 1) % len(hedges_face)]) # he_next
- heTable[i][3] = useful_vertices_list.index(he[0]) + 2 # vert source inner edges
- heTable[i][4] = useful_vertices_list.index(he[1]) + 2 # vert target inner edges
- heTable[i][7] = ordered_edges_inbound_faces.index(hedges_face) # face
- belongs_to_any_face = True
- break
- twin_idx = half_edges.index((he[1], he[0]))
- heTable[i][2] = twin_idx # he twin
- if not belongs_to_any_face:
- relevant_twins.append(twin_idx)
-
- angTable = np.ones(L_he // 2)
- for tidx in relevant_twins:
- angTable[tidx // 2] = rng.random() * (np.pi / 2 - 0.018) + 0.017
- heTable[tidx][5] = heTable[tidx][3] # vert source surface edges
- heTable[tidx][6] = heTable[tidx][4] # vert target surface edges
- heTable[tidx][3] = 0
- heTable[tidx][4] = 1
-
- if show:
- plot_bounded_mesh(
- vertTable,
- angTable,
- heTable,
- faceTable,
- L_box,
- path=path + "simulation_init/",
- name="simulation_init",
- save=True,
- )
-
- vertTable = jnp.array(vertTable)
- angTable = jnp.array(angTable)
- faceTable = jnp.array(faceTable)
- heTable = jnp.array(heTable)
-
- return vertTable, angTable, heTable, faceTable
-
- seeds = np.vstack([seeds, L_box * rng.random((1, 2))]) if success == 1 else L_box * rng.random((n_cells, 2)) # type: ignore
-
-
-def _fate_selection(faceTable: NDArray, n_fates: int, rng: Generator) -> NDArray:
- n_cells = faceTable.size
- n_cells_per_fate = n_cells // n_fates
- n_cells_left = n_cells % n_fates
- cell_fates = np.repeat(np.arange(n_fates), n_cells_per_fate)
- cell_fates = np.concatenate([cell_fates, np.arange(n_cells_left)])
- rng.shuffle(cell_fates)
- return np.hstack([faceTable, cell_fates[:, None]])
diff --git a/src/vertax/topo.py b/src/vertax/topo.py
index 5d42a1c..53c17f4 100644
--- a/src/vertax/topo.py
+++ b/src/vertax/topo.py
@@ -70,7 +70,7 @@ def body_fun(idx: Array, state: tuple[Array, Array, Array]) -> Array:
# check distance
distance = get_length(he_idx, vertTable_new, heTable_new, faceTable_new, width, height)
- # check if the two faces that share the hes are triangles
+ # check if the two faces that share the hes are triangles. We'll avoid a T1 transition in that case.
he_prev = he[0]
twin_he_prev = twin_he[0]
should_update = heTable_new[he_prev, 0] != he[1]
@@ -126,10 +126,6 @@ def update_state(_state: tuple[Array, Array, Array]) -> tuple[Array, Array, Arra
heTable_new = heTable_new.at[next_twin_next_he_idx, 6].add(-twin_he[6])
heTable_new = heTable_new.at[next_twin_next_he_idx, 7].add(-twin_he[7])
- ## vertTable
- vertTable_new = vertTable_new.at[he[3], 2].set(he_idx)
- vertTable_new = vertTable_new.at[twin_he[3], 2].set(twin_he_idx)
-
# he
x_source_he = vertTable_new[he[3], 0]
y_source_he = vertTable_new[he[3], 1]
diff --git a/tests/correlation/base_mesh.npz b/tests/correlation/base_mesh.npz
new file mode 100644
index 0000000..8c294d9
Binary files /dev/null and b/tests/correlation/base_mesh.npz differ
diff --git a/tests/correlation/input/areas_init.txt b/tests/correlation/input/areas_init.txt
new file mode 100644
index 0000000..759b65b
--- /dev/null
+++ b/tests/correlation/input/areas_init.txt
@@ -0,0 +1,20 @@
+0 1.0
+1 1.0
+2 1.0
+3 1.0
+4 1.0
+5 1.0
+6 1.0
+7 1.0
+8 1.0
+9 1.0
+10 1.0
+11 1.0
+12 1.0
+13 1.0
+14 1.0
+15 1.0
+16 1.0
+17 1.0
+18 1.0
+19 1.0
diff --git a/tests/correlation/input/faceTable.npy b/tests/correlation/input/faceTable.npy
new file mode 100644
index 0000000..eb0dfe9
Binary files /dev/null and b/tests/correlation/input/faceTable.npy differ
diff --git a/tests/correlation/input/heTable.npy b/tests/correlation/input/heTable.npy
new file mode 100644
index 0000000..e4e18cf
Binary files /dev/null and b/tests/correlation/input/heTable.npy differ
diff --git a/tests/correlation/input/line_tensions_init.txt b/tests/correlation/input/line_tensions_init.txt
new file mode 100644
index 0000000..0bfd7ee
--- /dev/null
+++ b/tests/correlation/input/line_tensions_init.txt
@@ -0,0 +1,120 @@
+0 1.2070553
+1 1.2070553
+2 1.373384
+3 1.373384
+4 1.1258662
+5 1.1258662
+6 1.3617237
+7 1.3617237
+8 1.1125065
+9 1.1125065
+10 1.318263
+11 1.318263
+12 0.95024586
+13 0.95024586
+14 1.1663989
+15 1.1663989
+16 1.1761924
+17 1.1761924
+18 1.4368774
+19 1.4368774
+20 1.2870216
+21 1.2870216
+22 1.3000536
+23 1.3000536
+24 1.2598435
+25 1.2598435
+26 1.0686067
+27 1.0686067
+28 1.0122381
+29 1.0122381
+30 1.6257486
+31 1.6257486
+32 1.4126803
+33 1.4126803
+34 1.1005847
+35 1.1005847
+36 1.2517841
+37 1.2517841
+38 0.94126415
+39 0.94126415
+40 1.2727275
+41 1.2727275
+42 0.670189
+43 0.670189
+44 0.8674402
+45 0.8674402
+46 1.3810965
+47 1.3810965
+48 1.4322762
+49 1.4322762
+50 1.0768814
+51 1.0768814
+52 1.3963214
+53 1.3963214
+54 0.9946632
+55 0.9946632
+56 1.0505323
+57 1.0505323
+58 1.0364997
+59 1.0364997
+60 1.1312494
+61 1.1312494
+62 1.6354012
+63 1.6354012
+64 1.1062915
+65 1.1062915
+66 1.34666
+67 1.34666
+68 1.2882904
+69 1.2882904
+70 1.22048
+71 1.22048
+72 1.0148377
+73 1.0148377
+74 1.2783688
+75 1.2783688
+76 1.3628558
+77 1.3628558
+78 1.088237
+79 1.088237
+80 1.5097132
+81 1.5097132
+82 1.3748161
+83 1.3748161
+84 1.3306218
+85 1.3306218
+86 1.2487597
+87 1.2487597
+88 1.4228945
+89 1.4228945
+90 1.2248588
+91 1.2248588
+92 1.4989245
+93 1.4989245
+94 1.1682049
+95 1.1682049
+96 1.3928055
+97 1.3928055
+98 1.0245054
+99 1.0245054
+100 1.2448698
+101 1.2448698
+102 0.953361
+103 0.953361
+104 1.0012536
+105 1.0012536
+106 1.0583911
+107 1.0583911
+108 1.1086955
+109 1.1086955
+110 1.4897367
+111 1.4897367
+112 1.3020321
+113 1.3020321
+114 1.5497851
+115 1.5497851
+116 1.0366948
+117 1.0366948
+118 1.4906743
+119 1.4906743
diff --git a/tests/correlation/input/vertTable.npy b/tests/correlation/input/vertTable.npy
new file mode 100644
index 0000000..bb56436
Binary files /dev/null and b/tests/correlation/input/vertTable.npy differ
diff --git a/tests/correlation/target/areas_target.txt b/tests/correlation/target/areas_target.txt
new file mode 100644
index 0000000..759b65b
--- /dev/null
+++ b/tests/correlation/target/areas_target.txt
@@ -0,0 +1,20 @@
+0 1.0
+1 1.0
+2 1.0
+3 1.0
+4 1.0
+5 1.0
+6 1.0
+7 1.0
+8 1.0
+9 1.0
+10 1.0
+11 1.0
+12 1.0
+13 1.0
+14 1.0
+15 1.0
+16 1.0
+17 1.0
+18 1.0
+19 1.0
diff --git a/tests/correlation/target/faceTable.npy b/tests/correlation/target/faceTable.npy
new file mode 100644
index 0000000..eb0dfe9
Binary files /dev/null and b/tests/correlation/target/faceTable.npy differ
diff --git a/tests/correlation/target/heTable.npy b/tests/correlation/target/heTable.npy
new file mode 100644
index 0000000..47806d4
Binary files /dev/null and b/tests/correlation/target/heTable.npy differ
diff --git a/tests/correlation/target/line_tensions_target.txt b/tests/correlation/target/line_tensions_target.txt
new file mode 100644
index 0000000..ac1087b
--- /dev/null
+++ b/tests/correlation/target/line_tensions_target.txt
@@ -0,0 +1,120 @@
+0 1.1906192
+1 1.1906192
+2 1.017332
+3 1.017332
+4 1.3207054
+5 1.3207054
+6 1.0616173
+7 1.0616173
+8 1.0205314
+9 1.0205314
+10 1.2739587
+11 1.2739587
+12 1.1592704
+13 1.1592704
+14 1.1485702
+15 1.1485702
+16 1.2769383
+17 1.2769383
+18 1.3720714
+19 1.3720714
+20 1.2822359
+21 1.2822359
+22 1.1936418
+23 1.1936418
+24 1.3022501
+25 1.3022501
+26 1.2443542
+27 1.2443542
+28 1.2808212
+29 1.2808212
+30 1.2033348
+31 1.2033348
+32 1.245045
+33 1.245045
+34 1.3494123
+35 1.3494123
+36 1.1960801
+37 1.1960801
+38 1.2285239
+39 1.2285239
+40 1.2501651
+41 1.2501651
+42 1.211172
+43 1.211172
+44 1.2574801
+45 1.2574801
+46 1.3949764
+47 1.3949764
+48 1.1188174
+49 1.1188174
+50 1.1663274
+51 1.1663274
+52 1.0817256
+53 1.0817256
+54 1.1406814
+55 1.1406814
+56 1.2019912
+57 1.2019912
+58 1.1227577
+59 1.1227577
+60 1.2112304
+61 1.2112304
+62 1.2413229
+63 1.2413229
+64 1.2296276
+65 1.2296276
+66 1.109159
+67 1.109159
+68 1.2114552
+69 1.2114552
+70 1.1198659
+71 1.1198659
+72 1.1236866
+73 1.1236866
+74 1.1237485
+75 1.1237485
+76 1.1073829
+77 1.1073829
+78 1.394489
+79 1.394489
+80 1.275705
+81 1.275705
+82 1.0322375
+83 1.0322375
+84 1.1951075
+85 1.1951075
+86 1.1569325
+87 1.1569325
+88 1.1006788
+89 1.1006788
+90 1.1215417
+91 1.1215417
+92 1.1633496
+93 1.1633496
+94 1.140002
+95 1.140002
+96 1.3191448
+97 1.3191448
+98 1.0277653
+99 1.0277653
+100 1.1872518
+101 1.1872518
+102 1.2968003
+103 1.2968003
+104 1.2248905
+105 1.2248905
+106 1.2747576
+107 1.2747576
+108 1.2983505
+109 1.2983505
+110 1.1821405
+111 1.1821405
+112 1.1954105
+113 1.1954105
+114 1.1715908
+115 1.1715908
+116 1.0611538
+117 1.0611538
+118 1.3598497
+119 1.3598497
diff --git a/tests/correlation/target/vertTable.npy b/tests/correlation/target/vertTable.npy
new file mode 100644
index 0000000..22d5300
Binary files /dev/null and b/tests/correlation/target/vertTable.npy differ
diff --git a/tests/correlation/target_mesh.npz b/tests/correlation/target_mesh.npz
new file mode 100644
index 0000000..752df33
Binary files /dev/null and b/tests/correlation/target_mesh.npz differ
diff --git a/tests/correlation/test_e2.py b/tests/correlation/test_e2.py
new file mode 100644
index 0000000..70b232b
--- /dev/null
+++ b/tests/correlation/test_e2.py
@@ -0,0 +1,319 @@
+"""Test the whole pipeline of bilevel optimization with the new API."""
+
+from __future__ import annotations
+
+import math
+from pathlib import Path
+from time import perf_counter
+from typing import TYPE_CHECKING
+
+import jax
+import jax.numpy as jnp
+import matplotlib.pyplot as plt
+import numpy as np
+import optax
+from jax import vmap
+from numpy.typing import NDArray
+
+from vertax import PbcBilevelOptimizer, PbcMesh, plot_mesh
+from vertax.cost import cost_v2v
+from vertax.geo import get_area, get_length
+from vertax.method_enum import BilevelOptimizationMethod
+
+if TYPE_CHECKING:
+ from jax import Array
+
+
+def load_geograph(path: str) -> tuple[Array, Array, Array]:
+ """Load a mesh the old way."""
+ return jnp.load(path + "vertTable.npy"), jnp.load(path + "heTable.npy"), jnp.load(path + "faceTable.npy")
+
+
+def translate_base_mesh() -> None:
+ """Translate old mesh data to new version."""
+ vertices, edges, faces = load_geograph("tests/correlation/input/")
+ vertices = vertices[:, :2]
+ mesh = PbcMesh.create_empty()
+ mesh.vertices = vertices
+ mesh.edges = edges.reshape(-1, 8)
+ mesh.faces = faces
+ mesh.width = math.sqrt(20)
+ mesh.height = math.sqrt(20)
+ mesh.vertices_params = jnp.asarray([0.0])
+ init_path = "tests/correlation/input/line_tensions_init.txt"
+ init_data = np.loadtxt(init_path)
+ init_values = init_data[:, 1]
+
+ he_params = jnp.asarray(init_values[::2])
+ mesh.edges_params = he_params
+ mesh.faces_params = jnp.asarray([0.0 for i in range(20)])
+ mesh.save_mesh("tests/correlation/base_mesh.npz")
+
+
+def translate_target_mesh() -> None:
+ """Translate old mesh data to new version (target mesh)."""
+ vertices, edges, faces = load_geograph("tests/correlation/target/")
+ vertices = vertices[:, :2]
+ mesh = PbcMesh.create_empty()
+ mesh.vertices = vertices
+ mesh.edges = edges.reshape(-1, 8)
+ mesh.faces = faces
+ mesh.width = math.sqrt(20)
+ mesh.height = math.sqrt(20)
+ mesh.save_mesh("tests/correlation/target_mesh.npz")
+
+
+def load_target_mesh() -> PbcMesh:
+ """Load target mesh."""
+ return PbcMesh.load_mesh("tests/correlation/target_mesh.npz")
+
+
+def load_base_mesh() -> PbcMesh:
+ """Load the base PBC mesh for correlation experiments."""
+ return PbcMesh.load_mesh("tests/correlation/base_mesh.npz")
+
+
+def create_optimizer() -> PbcBilevelOptimizer:
+ """Get the optimizer for the experiments."""
+ bop = PbcBilevelOptimizer()
+ bop.min_dist_T1 = 0.005
+ bop.max_nb_iterations = 1000
+ bop.tolerance = 0.00001
+ bop.patience = 5
+ bop.inner_solver = optax.sgd(learning_rate=0.01)
+ bop.outer_solver = optax.adam(learning_rate=0.0001, nesterov=True)
+ bop.bilevel_optimization_method = BilevelOptimizationMethod.AUTOMATIC_DIFFERENTIATION
+ bop.loss_function_outer = cost_v2v
+ return bop
+
+
+def load_areas_target() -> Array:
+ """Load target area for energy."""
+ init_path_target = "tests/correlation/target/areas_target.txt"
+ init_data_target = np.loadtxt(init_path_target)
+ init_values_target = init_data_target[:, 1]
+ return jnp.asarray(init_values_target)
+
+
+def load_tensions_target() -> Array:
+ """Load line tensions target for energy."""
+ init_path_target = "tests/correlation/target/line_tensions_target.txt"
+ init_data_target = np.loadtxt(init_path_target)
+ init_values_target = init_data_target[:, 1]
+ return jnp.asarray(init_values_target)
+
+
+def _load_and_sort(target_path: str, init_path: str) -> tuple[NDArray, NDArray]:
+ target_data = np.loadtxt(target_path)
+ init_data = np.loadtxt(init_path)
+
+ target_values = target_data[:, 1]
+ init_values = init_data[:, 1]
+
+ sorted_indices = np.argsort(target_values)
+ sorted_target_values = target_values[sorted_indices]
+ corresponding_init_values = init_values[sorted_indices]
+
+ return sorted_target_values, corresponding_init_values
+
+
+def _expected_result() -> None:
+ nb_epochs = 2_400
+ target1, init1 = _load_and_sort(
+ "OLD_E2_working/OLD_E2_working/2_inference_multiple/energy_line_tensions_COUPLED/target/line_tensions_target.txt",
+ "OLD_E2_working/OLD_E2_working/2_inference_multiple/energy_line_tensions_COUPLED/starting_0.1/line_tensions_init.txt",
+ )
+ _, axes = plt.subplots(1, 2, figsize=(14, 6), sharey=True)
+
+ # Plot for Initial Condition
+ axes[0].scatter(target1, init1, color="blue", alpha=0.7)
+ axes[0].plot([0.8, 1.7], [0.8, 1.7], color="red", linestyle="--", linewidth=1) # Bisecting line
+ axes[0].set_title("Initial Condition")
+ axes[0].set_xlabel("Sorted Target Values")
+ axes[0].set_ylabel("Corresponding Simulation Values")
+ axes[0].set_xlim(0.65, 1.85)
+ axes[0].set_ylim(0.65, 1.85)
+ axes[1].set_xlim(0.65, 1.85)
+ axes[1].set_ylim(0.65, 1.85)
+
+ # Calculate and display correlation coefficient for Initial Condition
+ corr1 = np.corrcoef(target1, init1)[0, 1]
+ axes[0].text(
+ 0.05, 0.95, f"Corr. coeff. = {corr1:.2f}", transform=axes[0].transAxes, fontsize=12, verticalalignment="top"
+ )
+
+ target2, init2 = _load_and_sort(
+ "OLD_E2_working/OLD_E2_working/2_inference_multiple/energy_line_tensions_COUPLED/target/line_tensions_target.txt",
+ f"OLD_E2_working/OLD_E2_working/2_inference_multiple/bilevel_opt_lines_LESS_COUPLED_0.1/configurations/{nb_epochs}/line_tensions_final.txt",
+ )
+
+ # Plot for Final Condition
+ axes[1].scatter(target2, init2, color="green", alpha=0.7)
+ axes[1].plot([0.8, 1.7], [0.8, 1.7], color="red", linestyle="--", linewidth=1) # Bisecting line
+ axes[1].set_title("Final Condition")
+ axes[1].set_xlabel("Sorted Target Values")
+ axes[1].set_ylabel("Corresponding Simulation Values")
+
+ # # Ensure both subplots share the same x and y limits
+ # x_min = min(min(target1)-0.05, min(init1)-0.05, min(target2)-0.05, min(init2)-0.05)
+ # x_max = max(max(target1)+0.05, max(init1)+0.05, max(target2)+0.05, max(init2)+0.05)
+ # y_min = x_min
+ # y_max = x_max
+
+ # Calculate and display correlation coefficient for Final Condition
+ corr2 = np.corrcoef(target2, init2)[0, 1]
+ axes[1].text(
+ 0.05, 0.95, f"Corr. coeff. = {corr2:.2f}", transform=axes[1].transAxes, fontsize=12, verticalalignment="top"
+ )
+
+ plt.show()
+
+
+def test_pearson_e2() -> None:
+ """Check identical result of a standard test with previous results (november 2025)."""
+ t_start = perf_counter()
+ nb_epochs = 10000
+ MAX_EDGES_IN_ANY_FACE = 20
+ areas_target = load_areas_target()
+
+ n_cells = 20
+ width = math.sqrt(n_cells)
+ height = width
+
+ # target_mesh = load_target_mesh()
+
+ bop = create_optimizer()
+
+ mesh_target = PbcMesh.from_random_seeds(nb_seeds=n_cells, width=width, height=height, random_key=1290)
+
+ # Initial condition (parameters)
+ mesh_target.vertices_params = jnp.asarray([0.0 for _ in range(mesh_target.nb_vertices)])
+
+ mu_tensions = 2.1
+ std_tensions = 0.3
+ key = jax.random.PRNGKey(543) # change the seed for different results
+ target_he_params = mu_tensions + std_tensions * jax.random.normal(key, shape=(mesh_target.nb_edges,))
+ # Set mesh parameters
+ mesh_target.edges_params = jnp.repeat(target_he_params, 2)
+
+ mesh_target.faces_params = jnp.asarray([1.0 for _ in range(mesh_target.nb_faces)])
+
+ he_params_reference = target_he_params[0]
+
+ mesh = PbcMesh.copy_mesh(mesh_target)
+ key = jax.random.PRNGKey(125) # change the seed for different results
+ # base_he_params = target_he_params + std_tensions * jax.random.normal(key, shape=(mesh.nb_edges,))
+ base_he_params = mu_tensions + std_tensions * jax.random.normal(key, shape=(mesh.nb_edges,))
+ # Set mesh parameters
+ mesh.edges_params = jnp.repeat(base_he_params, 2)
+
+ # Energy functions : Note that they use the width and height parameters now, defined earlier
+ def area_part(face: Array, _face_param: Array, vertTable: Array, heTable: Array, faceTable: Array) -> Array:
+ a = get_area(face, vertTable, heTable, faceTable, width, height, MAX_EDGES_IN_ANY_FACE)
+ # return (a - face_param) ** 2
+ return (a - areas_target[face]) ** 2
+
+ def hedge_part(he: Array, he_param: Array, vertTable: Array, heTable: Array, faceTable: Array) -> Array:
+ edge_lengths = get_length(he, vertTable, heTable, faceTable, width, height)
+ return he_param * edge_lengths
+
+ # It is important to define the energy function with this exact signature,
+ # even though the "_vert_params" is unused, we still keep it.
+ def energy(
+ vertTable: Array, heTable: Array, faceTable: Array, _vert_params: Array, he_params: Array, face_params: Array
+ ) -> Array:
+ K_areas = 20
+
+ def mapped_areas_part(face: Array, face_param: Array) -> Array:
+ return area_part(face, face_param, vertTable, heTable, faceTable)
+
+ def mapped_hedges_part(he: Array, he_param: Array) -> Array:
+ return hedge_part(he, he_param, vertTable, heTable, faceTable)
+
+ areas_part = vmap(mapped_areas_part)(jnp.arange(len(faceTable)), face_params)
+ hedges_part = vmap(mapped_hedges_part)(jnp.arange(2, len(heTable)), he_params[2:])
+ return (
+ (2 * he_params_reference * get_length(0, vertTable, heTable, faceTable, width, height))
+ + jnp.sum(hedges_part)
+ + (0.5 * K_areas) * jnp.sum(areas_part)
+ )
+
+ # Energy minimization (init cond equilibrium)
+ bop.loss_function_inner = energy
+ bop.inner_optimization(mesh_target)
+ bop.inner_optimization(mesh)
+ plot_mesh(mesh, show=False, save=True, save_path="tests/correlation/results/base_mesh.png", title="Base mesh")
+ plot_mesh(
+ mesh_target, show=False, save=True, save_path="tests/correlation/results/target_mesh.png", title="Target mesh"
+ )
+ mesh_target.save_mesh("tests/correlation/results/target_mesh.npz")
+
+ bop.vertices_target = mesh_target.vertices.copy()
+ bop.edges_target = mesh_target.edges.copy()
+ bop.faces_target = mesh_target.faces.copy()
+
+ def pearson_correlation(mesh: PbcMesh, _bop: PbcBilevelOptimizer) -> float:
+ return float(jnp.corrcoef(mesh.edges_params, mesh_target.edges_params)[0, 1])
+
+ bop.add_custom_metric("Pearson correlation", pearson_correlation)
+ bop.do_n_bilevel_optimization(
+ nb_epochs,
+ mesh,
+ report_every=10,
+ save_plotmesh_every=100,
+ save_mesh_data_every=100,
+ also_report_to_stdout=True,
+ save_folder="tests/correlation/results",
+ )
+ # for j in range(epochs + 1):
+ # t1 = perf_counter()
+ # print(
+ # "epoch: "
+ # + str(j)
+ # + "/"
+ # + str(epochs)
+ # + "\t cost: "
+ # + str(
+ # cost_v2v(
+ # pbc_mesh.vertices,
+ # pbc_mesh.edges,
+ # pbc_mesh.faces,
+ # pbc_mesh.width,
+ # pbc_mesh.height,
+ # bilevel_optimizer.vertices_target,
+ # bilevel_optimizer.edges_target,
+ # bilevel_optimizer.faces_target,
+ # )
+ # )
+ # )
+ #
+ # bilevel_optimizer.bilevel_optimization(pbc_mesh)
+ # print(perf_counter() - t1)
+ # pearson_corr = float(jnp.corrcoef(pbc_mesh.edges_params, pbc_mesh_target.edges_params)[0, 1])
+ # print("Pearson", pearson_corr)
+ # np_corr = np.corrcoef(pbc_mesh.edges_params, pbc_mesh_target.edges_params)[0, 1]
+ # print("Pearson np", np_corr)
+
+ t_end = perf_counter()
+ elapsed_times = t_end - t_start
+ print(f"Test correlation took {elapsed_times:.2f} s.")
+
+
+def read_result() -> None:
+ """Demonstrates how to extract tension data from saved meshes."""
+ # First get all mesh filenames.
+ mesh_filenames = Path("tests/correlation/results/meshes_data/").glob("mesh*.npz")
+ # Load mesh file and extract in particular the tensions (edges params)
+ tensions = [PbcMesh.load_mesh(str(filename)).edges_params for filename in mesh_filenames]
+ print(tensions[0])
+ print(tensions[-1])
+ print(f"{len(tensions)} tension arrays acquired.")
+
+
+if __name__ == "__main__":
+ # translate_base_mesh()
+ # translate_target_mesh()
+ # test_pearson_e2()
+ read_result()
+ # _expected_result()
+ # print(load_base_mesh().edges_params)
diff --git a/tests/correlation/test_e2_t1.py b/tests/correlation/test_e2_t1.py
new file mode 100644
index 0000000..cbb91f0
--- /dev/null
+++ b/tests/correlation/test_e2_t1.py
@@ -0,0 +1,342 @@
+"""Test the whole pipeline of bilevel optimization with the new API."""
+
+from __future__ import annotations
+
+import math
+from pathlib import Path
+from time import perf_counter
+from typing import TYPE_CHECKING
+
+import jax
+import jax.numpy as jnp
+import matplotlib.pyplot as plt
+import numpy as np
+import optax
+from jax import vmap
+from numpy.typing import NDArray
+
+from vertax import EdgePlot, FacePlot, PbcBilevelOptimizer, PbcMesh, cost_v2v, plot_mesh
+from vertax.geo import get_area, get_length
+from vertax.method_enum import BilevelOptimizationMethod
+
+if TYPE_CHECKING:
+ from jax import Array
+
+
+def load_geograph(path: str) -> tuple[Array, Array, Array]:
+ """Load a mesh the old way."""
+ return jnp.load(path + "vertTable.npy"), jnp.load(path + "heTable.npy"), jnp.load(path + "faceTable.npy")
+
+
+def translate_base_mesh() -> None:
+ """Translate old mesh data to new version."""
+ vertices, edges, faces = load_geograph("tests/correlation/input/")
+ vertices = vertices[:, :2]
+ mesh = PbcMesh.create_empty()
+ mesh.vertices = vertices
+ mesh.edges = edges.reshape(-1, 8)
+ mesh.faces = faces
+ mesh.width = math.sqrt(20)
+ mesh.height = math.sqrt(20)
+ mesh.vertices_params = jnp.asarray([0.0])
+ init_path = "tests/correlation/input/line_tensions_init.txt"
+ init_data = np.loadtxt(init_path)
+ init_values = init_data[:, 1]
+
+ he_params = jnp.asarray(init_values[::2])
+ mesh.edges_params = he_params
+ mesh.faces_params = jnp.asarray([0.0 for i in range(20)])
+ mesh.save_mesh("tests/correlation/base_mesh.npz")
+
+
+def translate_target_mesh() -> None:
+ """Translate old mesh data to new version (target mesh)."""
+ vertices, edges, faces = load_geograph("tests/correlation/target/")
+ vertices = vertices[:, :2]
+ mesh = PbcMesh.create_empty()
+ mesh.vertices = vertices
+ mesh.edges = edges.reshape(-1, 8)
+ mesh.faces = faces
+ mesh.width = math.sqrt(20)
+ mesh.height = math.sqrt(20)
+ mesh.save_mesh("tests/correlation/target_mesh.npz")
+
+
+def load_target_mesh() -> PbcMesh:
+ """Load target mesh."""
+ return PbcMesh.load_mesh("tests/correlation/target_mesh.npz")
+
+
+def load_base_mesh() -> PbcMesh:
+ """Load the base PBC mesh for correlation experiments."""
+ return PbcMesh.load_mesh("tests/correlation/base_mesh.npz")
+
+
+def create_optimizer() -> PbcBilevelOptimizer:
+ """Get the optimizer for the experiments."""
+ bop = PbcBilevelOptimizer()
+ bop.min_dist_T1 = 0.05
+ bop.max_nb_iterations = 1000
+ bop.tolerance = 0.00001
+ bop.patience = 5
+ bop.inner_solver = optax.sgd(learning_rate=0.01)
+ bop.outer_solver = optax.adam(learning_rate=0.0001, nesterov=True)
+ bop.bilevel_optimization_method = BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION
+ bop.loss_function_outer = cost_v2v
+ # bop.loss_function_outer = cost_v2v_ias
+ return bop
+
+
+def load_areas_target() -> Array:
+ """Load target area for energy."""
+ init_path_target = "tests/correlation/target/areas_target.txt"
+ init_data_target = np.loadtxt(init_path_target)
+ init_values_target = init_data_target[:, 1]
+ return jnp.asarray(init_values_target)
+
+
+def load_tensions_target() -> Array:
+ """Load line tensions target for energy."""
+ init_path_target = "tests/correlation/target/line_tensions_target.txt"
+ init_data_target = np.loadtxt(init_path_target)
+ init_values_target = init_data_target[:, 1]
+ return jnp.asarray(init_values_target)
+
+
+def _load_and_sort(target_path: str, init_path: str) -> tuple[NDArray, NDArray]:
+ target_data = np.loadtxt(target_path)
+ init_data = np.loadtxt(init_path)
+
+ target_values = target_data[:, 1]
+ init_values = init_data[:, 1]
+
+ sorted_indices = np.argsort(target_values)
+ sorted_target_values = target_values[sorted_indices]
+ corresponding_init_values = init_values[sorted_indices]
+
+ return sorted_target_values, corresponding_init_values
+
+
+def _expected_result() -> None:
+ nb_epochs = 2_400
+ target1, init1 = _load_and_sort(
+ "OLD_E2_working/OLD_E2_working/2_inference_multiple/energy_line_tensions_COUPLED/target/line_tensions_target.txt",
+ "OLD_E2_working/OLD_E2_working/2_inference_multiple/energy_line_tensions_COUPLED/starting_0.1/line_tensions_init.txt",
+ )
+ _, axes = plt.subplots(1, 2, figsize=(14, 6), sharey=True)
+
+ # Plot for Initial Condition
+ axes[0].scatter(target1, init1, color="blue", alpha=0.7)
+ axes[0].plot([0.8, 1.7], [0.8, 1.7], color="red", linestyle="--", linewidth=1) # Bisecting line
+ axes[0].set_title("Initial Condition")
+ axes[0].set_xlabel("Sorted Target Values")
+ axes[0].set_ylabel("Corresponding Simulation Values")
+ axes[0].set_xlim(0.65, 1.85)
+ axes[0].set_ylim(0.65, 1.85)
+ axes[1].set_xlim(0.65, 1.85)
+ axes[1].set_ylim(0.65, 1.85)
+
+ # Calculate and display correlation coefficient for Initial Condition
+ corr1 = np.corrcoef(target1, init1)[0, 1]
+ axes[0].text(
+ 0.05, 0.95, f"Corr. coeff. = {corr1:.2f}", transform=axes[0].transAxes, fontsize=12, verticalalignment="top"
+ )
+
+ target2, init2 = _load_and_sort(
+ "OLD_E2_working/OLD_E2_working/2_inference_multiple/energy_line_tensions_COUPLED/target/line_tensions_target.txt",
+ f"OLD_E2_working/OLD_E2_working/2_inference_multiple/bilevel_opt_lines_LESS_COUPLED_0.1/configurations/{nb_epochs}/line_tensions_final.txt",
+ )
+
+ # Plot for Final Condition
+ axes[1].scatter(target2, init2, color="green", alpha=0.7)
+ axes[1].plot([0.8, 1.7], [0.8, 1.7], color="red", linestyle="--", linewidth=1) # Bisecting line
+ axes[1].set_title("Final Condition")
+ axes[1].set_xlabel("Sorted Target Values")
+ axes[1].set_ylabel("Corresponding Simulation Values")
+
+ # # Ensure both subplots share the same x and y limits
+ # x_min = min(min(target1)-0.05, min(init1)-0.05, min(target2)-0.05, min(init2)-0.05)
+ # x_max = max(max(target1)+0.05, max(init1)+0.05, max(target2)+0.05, max(init2)+0.05)
+ # y_min = x_min
+ # y_max = x_max
+
+ # Calculate and display correlation coefficient for Final Condition
+ corr2 = np.corrcoef(target2, init2)[0, 1]
+ axes[1].text(
+ 0.05, 0.95, f"Corr. coeff. = {corr2:.2f}", transform=axes[1].transAxes, fontsize=12, verticalalignment="top"
+ )
+
+ plt.show()
+
+
+def test_pearson_e2_t1() -> None:
+ """Check identical result of a standard test with previous results (november 2025)."""
+ t_start = perf_counter()
+ Path("tests/correlation/results").mkdir(exist_ok=True)
+ nb_epochs = 10000
+ MAX_EDGES_IN_ANY_FACE = 20
+ areas_target = load_areas_target()
+
+ n_cells = 20
+ width = math.sqrt(n_cells)
+ height = width
+
+ # target_mesh = load_target_mesh()
+
+ bop = create_optimizer()
+
+ mesh_target = PbcMesh.from_random_seeds(nb_seeds=n_cells, width=width, height=height, random_key=1290)
+
+ # Initial condition (parameters)
+ mesh_target.vertices_params = jnp.asarray([0.0 for _ in range(mesh_target.nb_vertices)])
+
+ mu_tensions = 1.0
+ std_tensions = 0.2
+ key = jax.random.PRNGKey(643517) # change the seed for different results
+ target_he_params = mu_tensions + std_tensions * jax.random.normal(key, shape=(mesh_target.nb_edges,))
+ # Set mesh parameters
+ mesh_target.edges_params = jnp.repeat(target_he_params, 2)
+
+ mesh_target.faces_params = jnp.asarray([1.0 for _ in range(mesh_target.nb_faces)])
+
+ mesh = PbcMesh.copy_mesh(mesh_target)
+ key = jax.random.PRNGKey(238487) # change the seed for different results
+ # base_he_params = target_he_params + std_tensions * jax.random.normal(key, shape=(mesh.nb_edges,))
+ base_he_params = mu_tensions + std_tensions * jax.random.normal(key, shape=(mesh.nb_edges,))
+ # Set mesh parameters
+ mesh.edges_params = jnp.repeat(base_he_params, 2)
+
+ # Energy functions : Note that they use the width and height parameters now, defined earlier
+ def area_part(face: Array, _face_param: Array, vertTable: Array, heTable: Array, faceTable: Array) -> Array:
+ a = get_area(face, vertTable, heTable, faceTable, width, height, MAX_EDGES_IN_ANY_FACE)
+ # return (a - face_param) ** 2
+ return (a - areas_target[face]) ** 2
+
+ def hedge_part(he: Array, he_param: Array, vertTable: Array, heTable: Array, faceTable: Array) -> Array:
+ edge_lengths = get_length(he, vertTable, heTable, faceTable, width, height)
+ return he_param * edge_lengths
+
+ # It is important to define the energy function with this exact signature,
+ # even though the "_vert_params" is unused, we still keep it.
+ def energy(
+ vertTable: Array, heTable: Array, faceTable: Array, _vert_params: Array, he_params: Array, face_params: Array
+ ) -> Array:
+ K_areas = 20
+
+ def mapped_areas_part(face: Array, face_param: Array) -> Array:
+ return area_part(face, face_param, vertTable, heTable, faceTable)
+
+ def mapped_hedges_part(he: Array, he_param: Array) -> Array:
+ return hedge_part(he, he_param, vertTable, heTable, faceTable)
+
+ areas_part = vmap(mapped_areas_part)(jnp.arange(len(faceTable)), face_params)
+ hedges_part = vmap(mapped_hedges_part)(jnp.arange(len(heTable)), he_params)
+ soft_mean_part = (jnp.mean(he_params) - 1) ** 2
+ return jnp.sum(hedges_part) + (0.5 * K_areas) * jnp.sum(areas_part) + soft_mean_part
+
+ # Energy minimization (init cond equilibrium)
+ bop.loss_function_inner = energy
+ bop.inner_optimization(mesh_target)
+ bop.inner_optimization(mesh)
+ plot_mesh(mesh, show=False, save=True, save_path="tests/correlation/results/base_mesh.png", title="Base mesh")
+ plot_mesh(
+ mesh_target, show=False, save=True, save_path="tests/correlation/results/target_mesh.png", title="Target mesh"
+ )
+ mesh_target.save_mesh("tests/correlation/results/target_mesh.npz")
+
+ bop.vertices_target = mesh_target.vertices.copy()
+ bop.edges_target = mesh_target.edges.copy()
+ bop.faces_target = mesh_target.faces.copy()
+
+ def pearson_correlation(mesh: PbcMesh, _bop: PbcBilevelOptimizer) -> float:
+ return float(jnp.corrcoef(mesh.edges_params, mesh_target.edges_params)[0, 1])
+
+ bop.add_custom_metric("Pearson correlation", pearson_correlation)
+ bop.do_n_bilevel_optimization(
+ nb_epochs,
+ mesh,
+ report_every=10,
+ save_plotmesh_every=100,
+ save_mesh_data_every=100,
+ also_report_to_stdout=True,
+ save_folder="tests/correlation/results",
+ )
+ # for j in range(epochs + 1):
+ # t1 = perf_counter()
+ # print(
+ # "epoch: "
+ # + str(j)
+ # + "/"
+ # + str(epochs)
+ # + "\t cost: "
+ # + str(
+ # cost_v2v(
+ # pbc_mesh.vertices,
+ # pbc_mesh.edges,
+ # pbc_mesh.faces,
+ # pbc_mesh.width,
+ # pbc_mesh.height,
+ # bilevel_optimizer.vertices_target,
+ # bilevel_optimizer.edges_target,
+ # bilevel_optimizer.faces_target,
+ # )
+ # )
+ # )
+ #
+ # bilevel_optimizer.bilevel_optimization(pbc_mesh)
+ # print(perf_counter() - t1)
+ # pearson_corr = float(jnp.corrcoef(pbc_mesh.edges_params, pbc_mesh_target.edges_params)[0, 1])
+ # print("Pearson", pearson_corr)
+ # np_corr = np.corrcoef(pbc_mesh.edges_params, pbc_mesh_target.edges_params)[0, 1]
+ # print("Pearson np", np_corr)
+
+ t_end = perf_counter()
+ elapsed_times = t_end - t_start
+ print(f"Test correlation took {elapsed_times:.2f} s.")
+
+
+def read_result() -> None:
+ """Demonstrates how to extract tension data from saved meshes."""
+ # First get all mesh filenames.
+ mesh_filenames = Path("tests/correlation/results/meshes_data/").glob("mesh*.npz")
+ # Load mesh file and extract in particular the tensions (edges params)
+ tensions = [PbcMesh.load_mesh(str(filename)).edges_params for filename in mesh_filenames]
+ print(tensions[0])
+ print(tensions[-1])
+ print(f"{len(tensions)} tension arrays acquired.")
+
+
+def show_tensions() -> None:
+ """Find why an edge doesn't do good with IAS."""
+ for i in range(10):
+ step = 1200 + 100 * i
+ mesh = PbcMesh.load_mesh(f"tests/correlation/results_part1/meshes_data/mesh_epoch_{step}.npz")
+ plot_mesh(
+ mesh,
+ edge_plot=EdgePlot.EDGE_PARAMETER,
+ face_plot=FacePlot.WHITE,
+ edge_parameters_name="Tension",
+ title=f"Step {step} tensions",
+ show=False,
+ save=True,
+ save_path=f"tests/correlation/results_part1/tensions/mesh_epoch_{step}.png",
+ # forced_edge_scale=(0,2.)
+ )
+ # target_mesh = PbcMesh.load_mesh("tests/correlation/results_part1/target_mesh.npz")
+ # plot_mesh(
+ # target_mesh,
+ # edge_plot=EdgePlot.EDGE_PARAMETER,
+ # face_plot=FacePlot.WHITE,
+ # edge_parameters_name="Tension",
+ # title="Target Mesh tensions",
+ # )
+
+
+if __name__ == "__main__":
+ # show_tensions()
+ translate_base_mesh()
+ translate_target_mesh()
+ test_pearson_e2_t1()
+ # read_result()
+ # _expected_result()
+ # print(load_base_mesh().edges_params)
diff --git a/tests/correlation/test_e2_t1_part2.py b/tests/correlation/test_e2_t1_part2.py
new file mode 100644
index 0000000..744dd19
--- /dev/null
+++ b/tests/correlation/test_e2_t1_part2.py
@@ -0,0 +1,246 @@
+"""Test the whole pipeline of bilevel optimization with the new API, part 2 only v2v."""
+
+from __future__ import annotations
+
+import math
+from pathlib import Path
+from time import perf_counter
+from typing import TYPE_CHECKING
+
+import jax
+import jax.numpy as jnp
+import numpy as np
+import optax
+from jax import vmap
+
+from vertax import PbcBilevelOptimizer, PbcMesh, cost_v2v, plot_mesh
+from vertax.geo import get_area, get_length
+from vertax.method_enum import BilevelOptimizationMethod
+
+if TYPE_CHECKING:
+ from jax import Array
+
+
+def load_geograph(path: str) -> tuple[Array, Array, Array]:
+ """Load a mesh the old way."""
+ return jnp.load(path + "vertTable.npy"), jnp.load(path + "heTable.npy"), jnp.load(path + "faceTable.npy")
+
+
+def translate_base_mesh() -> None:
+ """Translate old mesh data to new version."""
+ vertices, edges, faces = load_geograph("tests/correlation/input/")
+ vertices = vertices[:, :2]
+ mesh = PbcMesh.create_empty()
+ mesh.vertices = vertices
+ mesh.edges = edges.reshape(-1, 8)
+ mesh.faces = faces
+ mesh.width = math.sqrt(20)
+ mesh.height = math.sqrt(20)
+ mesh.vertices_params = jnp.asarray([0.0])
+ init_path = "tests/correlation/input/line_tensions_init.txt"
+ init_data = np.loadtxt(init_path)
+ init_values = init_data[:, 1]
+
+ he_params = jnp.asarray(init_values[::2])
+ mesh.edges_params = he_params
+ mesh.faces_params = jnp.asarray([0.0 for i in range(20)])
+ mesh.save_mesh("tests/correlation/base_mesh.npz")
+
+
+def translate_target_mesh() -> None:
+ """Translate old mesh data to new version (target mesh)."""
+ vertices, edges, faces = load_geograph("tests/correlation/target/")
+ vertices = vertices[:, :2]
+ mesh = PbcMesh.create_empty()
+ mesh.vertices = vertices
+ mesh.edges = edges.reshape(-1, 8)
+ mesh.faces = faces
+ mesh.width = math.sqrt(20)
+ mesh.height = math.sqrt(20)
+ mesh.save_mesh("tests/correlation/target_mesh.npz")
+
+
+def load_target_mesh() -> PbcMesh:
+ """Load target mesh."""
+ return PbcMesh.load_mesh("tests/correlation/target_mesh.npz")
+
+
+def load_base_mesh() -> PbcMesh:
+ """Load the base PBC mesh for correlation experiments."""
+ return PbcMesh.load_mesh("tests/correlation/base_mesh.npz")
+
+
+def create_optimizer() -> PbcBilevelOptimizer:
+ """Get the optimizer for the experiments."""
+ bop = PbcBilevelOptimizer()
+ bop.min_dist_T1 = 0.05
+ bop.max_nb_iterations = 1000
+ bop.tolerance = 0.00001
+ bop.patience = 5
+ bop.inner_solver = optax.sgd(learning_rate=0.01)
+ bop.outer_solver = optax.adam(learning_rate=0.0001, nesterov=True)
+ bop.bilevel_optimization_method = BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION
+ bop.loss_function_outer = cost_v2v
+ # bop.loss_function_outer = cost_v2v_ias
+ return bop
+
+
+def load_areas_target() -> Array:
+ """Load target area for energy."""
+ init_path_target = "tests/correlation/target/areas_target.txt"
+ init_data_target = np.loadtxt(init_path_target)
+ init_values_target = init_data_target[:, 1]
+ return jnp.asarray(init_values_target)
+
+
+def load_tensions_target() -> Array:
+ """Load line tensions target for energy."""
+ init_path_target = "tests/correlation/target/line_tensions_target.txt"
+ init_data_target = np.loadtxt(init_path_target)
+ init_values_target = init_data_target[:, 1]
+ return jnp.asarray(init_values_target)
+
+
+def test_pearson_e2_t1() -> None:
+ """Check identical result of a standard test with previous results (november 2025)."""
+ t_start = perf_counter()
+ Path("tests/correlation/results").mkdir(exist_ok=True)
+ nb_epochs = 10000
+ MAX_EDGES_IN_ANY_FACE = 20
+ areas_target = load_areas_target()
+
+ n_cells = 20
+ width = math.sqrt(n_cells)
+ height = width
+
+ # target_mesh = load_target_mesh()
+
+ bop = create_optimizer()
+
+ mesh_target = PbcMesh.from_random_seeds(nb_seeds=n_cells, width=width, height=height, random_key=1290)
+
+ # Initial condition (parameters)
+ mesh_target.vertices_params = jnp.asarray([0.0 for _ in range(mesh_target.nb_vertices)])
+
+ mu_tensions = 1.0
+ std_tensions = 0.2
+ key = jax.random.PRNGKey(643517) # change the seed for different results
+ target_he_params = mu_tensions + std_tensions * jax.random.normal(key, shape=(mesh_target.nb_edges,))
+ # Set mesh parameters
+ mesh_target.edges_params = jnp.repeat(target_he_params, 2)
+
+ mesh_target.faces_params = jnp.asarray([1.0 for _ in range(mesh_target.nb_faces)])
+
+ he_params_reference = target_he_params[0]
+
+ mesh = PbcMesh.load_mesh("tests/correlation/results_part1/meshes_data/mesh_epoch_1500.npz")
+
+ # Energy functions : Note that they use the width and height parameters now, defined earlier
+ def area_part(face: Array, _face_param: Array, vertTable: Array, heTable: Array, faceTable: Array) -> Array:
+ a = get_area(face, vertTable, heTable, faceTable, width, height, MAX_EDGES_IN_ANY_FACE)
+ # return (a - face_param) ** 2
+ return (a - areas_target[face]) ** 2
+
+ def hedge_part(he: Array, he_param: Array, vertTable: Array, heTable: Array, faceTable: Array) -> Array:
+ edge_lengths = get_length(he, vertTable, heTable, faceTable, width, height)
+ return he_param * edge_lengths
+
+ # It is important to define the energy function with this exact signature,
+ # even though the "_vert_params" is unused, we still keep it.
+ def energy(
+ vertTable: Array, heTable: Array, faceTable: Array, _vert_params: Array, he_params: Array, face_params: Array
+ ) -> Array:
+ K_areas = 20
+
+ def mapped_areas_part(face: Array, face_param: Array) -> Array:
+ return area_part(face, face_param, vertTable, heTable, faceTable)
+
+ def mapped_hedges_part(he: Array, he_param: Array) -> Array:
+ return hedge_part(he, he_param, vertTable, heTable, faceTable)
+
+ areas_part = vmap(mapped_areas_part)(jnp.arange(len(faceTable)), face_params)
+ hedges_part = vmap(mapped_hedges_part)(jnp.arange(2, len(heTable)), he_params[2:])
+ return (
+ (2 * he_params_reference * get_length(0, vertTable, heTable, faceTable, width, height))
+ + jnp.sum(hedges_part)
+ + (0.5 * K_areas) * jnp.sum(areas_part)
+ )
+
+ # Energy minimization (init cond equilibrium)
+ bop.loss_function_inner = energy
+ bop.inner_optimization(mesh_target)
+ plot_mesh(mesh, show=False, save=True, save_path="tests/correlation/results/base_mesh.png", title="Base mesh")
+ plot_mesh(
+ mesh_target, show=False, save=True, save_path="tests/correlation/results/target_mesh.png", title="Target mesh"
+ )
+ mesh_target.save_mesh("tests/correlation/results/target_mesh.npz")
+
+ bop.vertices_target = mesh_target.vertices.copy()
+ bop.edges_target = mesh_target.edges.copy()
+ bop.faces_target = mesh_target.faces.copy()
+
+ def pearson_correlation(mesh: PbcMesh, _bop: PbcBilevelOptimizer) -> float:
+ return float(jnp.corrcoef(mesh.edges_params, mesh_target.edges_params)[0, 1])
+
+ bop.add_custom_metric("Pearson correlation", pearson_correlation)
+ bop.do_n_bilevel_optimization(
+ nb_epochs,
+ mesh,
+ report_every=10,
+ save_plotmesh_every=100,
+ save_mesh_data_every=100,
+ also_report_to_stdout=True,
+ save_folder="tests/correlation/results",
+ )
+ # for j in range(epochs + 1):
+ # t1 = perf_counter()
+ # print(
+ # "epoch: "
+ # + str(j)
+ # + "/"
+ # + str(epochs)
+ # + "\t cost: "
+ # + str(
+ # cost_v2v(
+ # pbc_mesh.vertices,
+ # pbc_mesh.edges,
+ # pbc_mesh.faces,
+ # pbc_mesh.width,
+ # pbc_mesh.height,
+ # bilevel_optimizer.vertices_target,
+ # bilevel_optimizer.edges_target,
+ # bilevel_optimizer.faces_target,
+ # )
+ # )
+ # )
+ #
+ # bilevel_optimizer.bilevel_optimization(pbc_mesh)
+ # print(perf_counter() - t1)
+ # pearson_corr = float(jnp.corrcoef(pbc_mesh.edges_params, pbc_mesh_target.edges_params)[0, 1])
+ # print("Pearson", pearson_corr)
+ # np_corr = np.corrcoef(pbc_mesh.edges_params, pbc_mesh_target.edges_params)[0, 1]
+ # print("Pearson np", np_corr)
+
+ t_end = perf_counter()
+ elapsed_times = t_end - t_start
+ print(f"Test correlation took {elapsed_times:.2f} s.")
+
+
+def read_result() -> None:
+ """Demonstrates how to extract tension data from saved meshes."""
+ # First get all mesh filenames.
+ mesh_filenames = Path("tests/correlation/results/meshes_data/").glob("mesh*.npz")
+ # Load mesh file and extract in particular the tensions (edges params)
+ tensions = [PbcMesh.load_mesh(str(filename)).edges_params for filename in mesh_filenames]
+ print(tensions[0])
+ print(tensions[-1])
+ print(f"{len(tensions)} tension arrays acquired.")
+
+
+if __name__ == "__main__":
+ translate_base_mesh()
+ translate_target_mesh()
+ test_pearson_e2_t1()
+ # read_result()
+ # _expected_result()
+ # print(load_base_mesh().edges_params)
diff --git a/tests/test_cpu_vs_gpu.py b/tests/test_cpu_vs_gpu.py
index 40820a5..5ba6773 100644
--- a/tests/test_cpu_vs_gpu.py
+++ b/tests/test_cpu_vs_gpu.py
@@ -49,7 +49,7 @@ def perform_bilevel_opt(n_cells: int, n_epochs: int) -> float: # noqa: C901
MAX_EDGES_IN_ANY_FACE = 20
# Set periodic boundary mesh and some of its properties
- pbc_mesh = PbcMesh.periodic_voronoi_from_random_seeds(nb_seeds=n_cells, width=width, height=height, random_key=0)
+ pbc_mesh = PbcMesh.from_random_seeds(nb_seeds=n_cells, width=width, height=height, random_key=0)
# Note: those are base values so the following can be omitted
bilevel_optimizer = PbcBilevelOptimizer()
bilevel_optimizer.min_dist_T1 = 0.005
diff --git a/tests/test_forward_modeling.py b/tests/test_forward_modeling.py
index 1d90db4..17d475b 100644
--- a/tests/test_forward_modeling.py
+++ b/tests/test_forward_modeling.py
@@ -27,7 +27,7 @@ def test_forward_modeling_for_regressions() -> None:
L_box = jnp.sqrt(n_cells)
width = float(L_box)
height = float(L_box)
- pbc_mesh = PbcMesh.periodic_voronoi_from_random_seeds(nb_seeds=n_cells, width=width, height=height, random_key=1)
+ pbc_mesh = PbcMesh.from_random_seeds(nb_seeds=n_cells, width=width, height=height, random_key=1)
# A mesh is not only vertices, edges and faces. We can attach parameters to them.
pbc_mesh.vertices_params = jnp.asarray([0.0])
pbc_mesh.edges_params = jnp.asarray([0.0])
diff --git a/tests/test_forward_modeling_bounded.py b/tests/test_forward_modeling_bounded.py
index cde1955..b807a9c 100644
--- a/tests/test_forward_modeling_bounded.py
+++ b/tests/test_forward_modeling_bounded.py
@@ -6,7 +6,7 @@
from numpy.testing import assert_allclose
from vertax import BoundedBilevelOptimizer, BoundedMesh
-from vertax.energy import energy_bounded
+from vertax.energy import energy_line_tensions_bounded
def test_regression() -> None:
@@ -34,7 +34,7 @@ def test_regression() -> None:
bilevel_optimizer.patience = 5
# Energy minimization
- bilevel_optimizer.loss_function_inner = energy_bounded
+ bilevel_optimizer.loss_function_inner = energy_line_tensions_bounded
bilevel_optimizer.inner_optimization(mesh=bounded_mesh)
saved_path = "tests/reference_result_test_forward_modeling_bounded.npz"
diff --git a/tests/test_inverse_modeling.py b/tests/test_inverse_modeling.py
index 32e6507..fddc28e 100644
--- a/tests/test_inverse_modeling.py
+++ b/tests/test_inverse_modeling.py
@@ -33,7 +33,7 @@ def test_inverse_modeling_for_regressions() -> None: # noqa: C901
MAX_EDGES_IN_ANY_FACE = 20
# Set periodic boundary mesh and some of its properties
- pbc_mesh = PbcMesh.periodic_voronoi_from_random_seeds(nb_seeds=n_cells, width=width, height=height, random_key=0)
+ pbc_mesh = PbcMesh.from_random_seeds(nb_seeds=n_cells, width=width, height=height, random_key=0)
# Initial condition (parameters)
pbc_mesh.vertices_params = jnp.asarray([0.0 for _ in range(pbc_mesh.nb_vertices)])
diff --git a/tests/test_inverse_modeling_bounded.py b/tests/test_inverse_modeling_bounded.py
index 7300eab..9c9558b 100644
--- a/tests/test_inverse_modeling_bounded.py
+++ b/tests/test_inverse_modeling_bounded.py
@@ -8,7 +8,7 @@
from vertax import BoundedBilevelOptimizer, BoundedMesh
from vertax.cost import cost_ratio
-from vertax.energy import energy_bounded
+from vertax.energy import energy_line_tensions_bounded
from vertax.method_enum import BilevelOptimizationMethod
@@ -40,7 +40,7 @@ def test_regression() -> None:
bilevel_optimizer.bilevel_optimization_method = BilevelOptimizationMethod.EQUILIBRIUM_PROPAGATION
# Energy minimization
- bilevel_optimizer.loss_function_inner = energy_bounded
+ bilevel_optimizer.loss_function_inner = energy_line_tensions_bounded
bilevel_optimizer.inner_optimization(mesh=bounded_mesh)
bilevel_optimizer.loss_function_outer = cost_ratio
diff --git a/tests/test_mesh.py b/tests/test_mesh.py
deleted file mode 100644
index 1894fe8..0000000
--- a/tests/test_mesh.py
+++ /dev/null
@@ -1,91 +0,0 @@
-"""Private test module to test implementation of a new Mesh class."""
-
-import os
-
-os.environ["JAX_PLATFORM_NAME"] = "cpu"
-import jax.numpy as jnp
-import jax.random
-import pytest
-from numpy.testing import assert_array_equal
-from tifffile import imread
-
-from vertax import PbcMesh, plot_mesh
-from vertax.start import create_mesh_from_image, create_mesh_from_seeds
-
-
-def test_private_constructor_mesh() -> None:
- """Check that a PBCMesh has a private constructor."""
- with pytest.raises(TypeError):
- PbcMesh()
-
-
-def test_mesh_can_be_privately_created() -> None:
- """Check that a PBCMesh has a private _create function."""
- my_mesh = PbcMesh._create()
- assert hasattr(my_mesh, "vertices")
- assert hasattr(my_mesh, "edges")
- assert hasattr(my_mesh, "faces")
-
-
-def test_compare_pbc_mesh_with_create_mesh_from_seeds() -> None:
- """Compare the two from_seeds functions. They should be the same."""
- # Initial condition
- n_cells = 100
- key = jax.random.PRNGKey(1)
- L_box = jnp.sqrt(n_cells)
- width = float(L_box)
- height = float(L_box)
- seeds = L_box * jax.random.uniform(key, (n_cells, 2))
- vertTable, heTable, faceTable = create_mesh_from_seeds(seeds)
-
- my_mesh = PbcMesh.periodic_voronoi_from_random_seeds(n_cells, width, height, random_key=1)
-
- assert_array_equal(my_mesh.vertices, vertTable)
- assert_array_equal(my_mesh.edges, heTable)
- assert_array_equal(my_mesh.faces, faceTable)
-
-
-@pytest.mark.long
-def test_compare_pbc_mesh_with_create_mesh_from_image() -> None:
- """Compare the two from_seeds functions. They should be the same."""
- plot = False
- # imread tiff = Y is the first axis, X the second.
- img = imread("tests/test_image.tif") # [:-101, :] # non rect, odd and pair dimensions
- print("create mesh from image...")
- vertTable, heTable, faceTable = create_mesh_from_image(img)
- if plot:
- plot_mesh(
- vertTable,
- heTable,
- faceTable,
- width=2 * img.shape[1],
- height=2 * img.shape[0],
- path="ref_image",
- save=True,
- show=False,
- )
-
- print("PBC now...")
- my_mesh = PbcMesh.periodic_from_image(img)
- if plot:
- plot_mesh(
- my_mesh.vertices,
- my_mesh.edges,
- my_mesh.faces,
- width=2 * img.shape[1],
- height=2 * img.shape[0],
- path="pbc_image",
- save=True,
- show=False,
- )
-
- assert_array_equal(my_mesh.vertices, vertTable)
- assert_array_equal(my_mesh.edges, heTable)
- assert_array_equal(my_mesh.faces, faceTable)
-
-
-if __name__ == "__main__":
- test_private_constructor_mesh()
- test_mesh_can_be_privately_created()
- test_compare_pbc_mesh_with_create_mesh_from_seeds()
- test_compare_pbc_mesh_with_create_mesh_from_image()
diff --git a/tests/test_n_bilevel_opt.py b/tests/test_n_bilevel_opt.py
index a554f5a..b1e732a 100644
--- a/tests/test_n_bilevel_opt.py
+++ b/tests/test_n_bilevel_opt.py
@@ -29,7 +29,7 @@ def test_n_bilevel_opt() -> None: # noqa: C901
MAX_EDGES_IN_ANY_FACE = 20
# Set periodic boundary mesh and some of its properties
- pbc_mesh = PbcMesh.periodic_voronoi_from_random_seeds(nb_seeds=n_cells, width=width, height=height, random_key=0)
+ pbc_mesh = PbcMesh.from_random_seeds(nb_seeds=n_cells, width=width, height=height, random_key=0)
# Note: those are base values so the following can be omitted
bilevel_optimizer = PbcBilevelOptimizer()
bilevel_optimizer.min_dist_T1 = 0.005
diff --git a/tests/test_n_bilevel_opt_bounded.py b/tests/test_n_bilevel_opt_bounded.py
index af87b8f..63838d6 100644
--- a/tests/test_n_bilevel_opt_bounded.py
+++ b/tests/test_n_bilevel_opt_bounded.py
@@ -10,7 +10,7 @@
from vertax import BoundedBilevelOptimizer, BoundedMesh
from vertax.cost import cost_ratio
-from vertax.energy import energy_bounded
+from vertax.energy import energy_line_tensions_bounded
from vertax.method_enum import BilevelOptimizationMethod
if TYPE_CHECKING:
@@ -48,7 +48,7 @@ def test_n_bilevel_opt() -> None:
bounded_mesh.faces_params = jnp.asarray([0.0 for _ in range(bounded_mesh.nb_faces)])
# Energy minimization (init cond equilibrium)
- bilevel_optimizer.loss_function_inner = energy_bounded
+ bilevel_optimizer.loss_function_inner = energy_line_tensions_bounded
bilevel_optimizer.inner_optimization(mesh=bounded_mesh)
# If you want to select only a subset of vertices, edges, and faces, it's possible:
# pbc_mesh.inner_opt(
@@ -60,7 +60,7 @@ def test_n_bilevel_opt() -> None:
def energy_metric(mesh: BoundedMesh, _bilevel_opt: BoundedBilevelOptimizer) -> float:
return float(
- energy_bounded(
+ energy_line_tensions_bounded(
mesh.vertices,
mesh.angles,
mesh.edges,
diff --git a/tests/test_plot.py b/tests/test_plot.py
index 0c30ab1..405d440 100644
--- a/tests/test_plot.py
+++ b/tests/test_plot.py
@@ -34,9 +34,10 @@ def test_plot() -> None:
face_plot=FacePlot.PERIMETER,
vertex_parameters_name="Random vertex parameter",
title="Too much colorbar is possible !",
+ forced_edge_scale=(-1, 1),
)
- pbc_mesh = PbcMesh.periodic_voronoi_from_random_seeds(nb_seeds=n_cells, width=width, height=height, random_key=1)
+ pbc_mesh = PbcMesh.from_random_seeds(nb_seeds=n_cells, width=width, height=height, random_key=1)
rng = np.random.default_rng(1337)
pbc_mesh.vertices_params = jnp.array(rng.random(pbc_mesh.nb_vertices) * 82 + 14)
pbc_mesh.edges_params = jnp.array(rng.random(pbc_mesh.nb_edges) * 3 + 1)
@@ -48,6 +49,8 @@ def test_plot() -> None:
face_plot=FacePlot.AREA,
vertex_parameters_name="Random vertex parameter",
title="Too much colorbar is possible !",
+ forced_vertex_scale=(0, 100),
+ forced_face_scale=(0, 10),
)
diff --git a/tests/test_plot_rectangular.py b/tests/test_plot_rectangular.py
index 636b772..e55fc48 100644
--- a/tests/test_plot_rectangular.py
+++ b/tests/test_plot_rectangular.py
@@ -11,7 +11,7 @@ def show_rectangular_mesh() -> None:
height = 10
# Initial condition
- mesh = PbcMesh.periodic_voronoi_from_random_seeds(n_cells, width, height, random_key=1)
+ mesh = PbcMesh.from_random_seeds(n_cells, width, height, random_key=1)
plot_mesh(mesh)
diff --git a/typos.toml b/typos.toml
new file mode 100644
index 0000000..fab5d09
--- /dev/null
+++ b/typos.toml
@@ -0,0 +1,4 @@
+[default]
+
+[default.extend-words]
+arange = "arange"
diff --git a/uv.lock b/uv.lock
index b96cf41..900c839 100644
--- a/uv.lock
+++ b/uv.lock
@@ -2,36 +2,36 @@ version = 1
revision = 3
requires-python = ">=3.11"
resolution-markers = [
- "python_full_version >= '3.14' and sys_platform == 'darwin'",
- "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'darwin'",
- "python_full_version >= '3.14' and platform_machine == 'aarch64' and sys_platform == 'linux'",
- "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine == 'aarch64' and sys_platform == 'linux'",
"python_full_version >= '3.14' and sys_platform == 'win32'",
"python_full_version >= '3.14' and sys_platform == 'emscripten'",
+ "python_full_version >= '3.14' and sys_platform == 'darwin'",
+ "python_full_version >= '3.14' and platform_machine == 'aarch64' and sys_platform == 'linux'",
"(python_full_version >= '3.14' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.14' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'linux' and sys_platform != 'win32')",
"python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'win32'",
"python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'",
+ "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'darwin'",
+ "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine == 'aarch64' and sys_platform == 'linux'",
"(python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'linux' and sys_platform != 'win32')",
- "python_full_version < '3.12' and sys_platform == 'darwin'",
- "python_full_version < '3.12' and platform_machine == 'aarch64' and sys_platform == 'linux'",
"python_full_version < '3.12' and sys_platform == 'win32'",
"python_full_version < '3.12' and sys_platform == 'emscripten'",
+ "python_full_version < '3.12' and sys_platform == 'darwin'",
+ "python_full_version < '3.12' and platform_machine == 'aarch64' and sys_platform == 'linux'",
"(python_full_version < '3.12' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.12' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'linux' and sys_platform != 'win32')",
]
[[package]]
name = "absl-py"
-version = "2.3.1"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/10/2a/c93173ffa1b39c1d0395b7e842bbdc62e556ca9d8d3b5572926f3e4ca752/absl_py-2.3.1.tar.gz", hash = "sha256:a97820526f7fbfd2ec1bce83f3f25e3a14840dac0d8e02a0b71cd75db3f77fc9", size = 116588, upload-time = "2025-07-03T09:31:44.05Z" }
+version = "2.4.0"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/64/c7/8de93764ad66968d19329a7e0c147a2bb3c7054c554d4a119111b8f9440f/absl_py-2.4.0.tar.gz", hash = "sha256:8c6af82722b35cf71e0f4d1d47dcaebfff286e27110a99fc359349b247dfb5d4", size = 116543, upload-time = "2026-01-28T10:17:05.322Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/8f/aa/ba0014cc4659328dc818a28827be78e6d97312ab0cb98105a770924dc11e/absl_py-2.3.1-py3-none-any.whl", hash = "sha256:eeecf07f0c2a93ace0772c92e596ace6d3d3996c042b2128459aaae2a76de11d", size = 135811, upload-time = "2025-07-03T09:31:42.253Z" },
+ { url = "https://files.pythonhosted.org/packages/18/a6/907a406bb7d359e6a63f99c313846d9eec4f7e6f7437809e03aa00fa3074/absl_py-2.4.0-py3-none-any.whl", hash = "sha256:88476fd881ca8aab94ffa78b7b6c632a782ab3ba1cd19c9bd423abc4fb4cd28d", size = 135750, upload-time = "2026-01-28T10:17:04.19Z" },
]
[[package]]
name = "annotated-types"
version = "0.7.0"
-source = { registry = "https://pypi.org/simple" }
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" },
@@ -40,7 +40,7 @@ wheels = [
[[package]]
name = "cellpose"
version = "3.1.0"
-source = { registry = "https://pypi.org/simple" }
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
dependencies = [
{ name = "fastremap" },
{ name = "imagecodecs" },
@@ -63,7 +63,7 @@ wheels = [
[[package]]
name = "chex"
version = "0.1.90"
-source = { registry = "https://pypi.org/simple" }
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
dependencies = [
{ name = "absl-py" },
{ name = "jax" },
@@ -81,7 +81,7 @@ wheels = [
[[package]]
name = "cloudpickle"
version = "3.1.2"
-source = { registry = "https://pypi.org/simple" }
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/27/fb/576f067976d320f5f0114a8d9fa1215425441bb35627b1993e5afd8111e5/cloudpickle-3.1.2.tar.gz", hash = "sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414", size = 22330, upload-time = "2025-11-03T09:25:26.604Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl", hash = "sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a", size = 22228, upload-time = "2025-11-03T09:25:25.534Z" },
@@ -90,7 +90,7 @@ wheels = [
[[package]]
name = "colorama"
version = "0.4.6"
-source = { registry = "https://pypi.org/simple" }
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
@@ -99,7 +99,7 @@ wheels = [
[[package]]
name = "contourpy"
version = "1.3.3"
-source = { registry = "https://pypi.org/simple" }
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
dependencies = [
{ name = "numpy" },
]
@@ -178,10 +178,81 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/0c/58/bd257695f39d05594ca4ad60df5bcb7e32247f9951fd09a9b8edb82d1daa/contourpy-1.3.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:3d1a3799d62d45c18bafd41c5fa05120b96a28079f2393af559b843d1a966a77", size = 225315, upload-time = "2025-07-26T12:02:58.801Z" },
]
+[[package]]
+name = "cuda-bindings"
+version = "13.2.0"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
+dependencies = [
+ { name = "cuda-pathfinder", marker = "sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'" },
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e0/a9/3a8241c6e19483ac1f1dcf5c10238205dcb8a6e9d0d4d4709240dff28ff4/cuda_bindings-13.2.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:721104c603f059780d287969be3d194a18d0cc3b713ed9049065a1107706759d", size = 5730273, upload-time = "2026-03-11T00:12:37.18Z" },
+ { url = "https://files.pythonhosted.org/packages/e9/94/2748597f47bb1600cd466b20cab4159f1530a3a33fe7f70fee199b3abb9e/cuda_bindings-13.2.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1eba9504ac70667dd48313395fe05157518fd6371b532790e96fbb31bbb5a5e1", size = 6313924, upload-time = "2026-03-11T00:12:39.462Z" },
+ { url = "https://files.pythonhosted.org/packages/52/c8/b2589d68acf7e3d63e2be330b84bc25712e97ed799affbca7edd7eae25d6/cuda_bindings-13.2.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e865447abfb83d6a98ad5130ed3c70b1fc295ae3eeee39fd07b4ddb0671b6788", size = 5722404, upload-time = "2026-03-11T00:12:44.041Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/92/f899f7bbb5617bb65ec52a6eac1e9a1447a86b916c4194f8a5001b8cde0c/cuda_bindings-13.2.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:46d8776a55d6d5da9dd6e9858fba2efcda2abe6743871dee47dd06eb8cb6d955", size = 6320619, upload-time = "2026-03-11T00:12:45.939Z" },
+ { url = "https://files.pythonhosted.org/packages/df/93/eef988860a3ca985f82c4f3174fc0cdd94e07331ba9a92e8e064c260337f/cuda_bindings-13.2.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6629ca2df6f795b784752409bcaedbd22a7a651b74b56a165ebc0c9dcbd504d0", size = 5614610, upload-time = "2026-03-11T00:12:50.337Z" },
+ { url = "https://files.pythonhosted.org/packages/18/23/6db3aba46864aee357ab2415135b3fe3da7e9f1fa0221fa2a86a5968099c/cuda_bindings-13.2.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7dca0da053d3b4cc4869eff49c61c03f3c5dbaa0bcd712317a358d5b8f3f385d", size = 6149914, upload-time = "2026-03-11T00:12:52.374Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/87/87a014f045b77c6de5c8527b0757fe644417b184e5367db977236a141602/cuda_bindings-13.2.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a6464b30f46692d6c7f65d4a0e0450d81dd29de3afc1bb515653973d01c2cd6e", size = 5685673, upload-time = "2026-03-11T00:12:56.371Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/5e/c0fe77a73aaefd3fff25ffaccaac69c5a63eafdf8b9a4c476626ef0ac703/cuda_bindings-13.2.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f4af9f3e1be603fa12d5ad6cfca7844c9d230befa9792b5abdf7dd79979c3626", size = 6191386, upload-time = "2026-03-11T00:12:58.965Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/58/ed2c3b39c8dd5f96aa7a4abef0d47a73932c7a988e30f5fa428f00ed0da1/cuda_bindings-13.2.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df850a1ff8ce1b3385257b08e47b70e959932f5f432d0a4e46a355962b4e4771", size = 5507469, upload-time = "2026-03-11T00:13:04.063Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/01/0c941b112ceeb21439b05895eace78ca1aa2eaaf695c8521a068fd9b4c00/cuda_bindings-13.2.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8a16384c6494e5485f39314b0b4afb04bee48d49edb16d5d8593fd35bbd231b", size = 6059693, upload-time = "2026-03-11T00:13:06.003Z" },
+]
+
+[[package]]
+name = "cuda-pathfinder"
+version = "1.5.3"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d3/d6/ac63065d33dd700fee7ebd7d287332401b54e31b9346e142f871e1f0b116/cuda_pathfinder-1.5.3-py3-none-any.whl", hash = "sha256:dff021123aedbb4117cc7ec81717bbfe198fb4e8b5f1ee57e0e084fec5c8577d", size = 49991, upload-time = "2026-04-14T20:09:27.037Z" },
+]
+
+[[package]]
+name = "cuda-toolkit"
+version = "13.0.2"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/57/b2/453099f5f3b698d7d0eab38916aac44c7f76229f451709e2eb9db6615dcd/cuda_toolkit-13.0.2-py2.py3-none-any.whl", hash = "sha256:b198824cf2f54003f50d64ada3a0f184b42ca0846c1c94192fa269ecd97a66eb", size = 2364, upload-time = "2025-12-19T23:24:07.328Z" },
+]
+
+[package.optional-dependencies]
+cublas = [
+ { name = "nvidia-cublas", marker = "sys_platform == 'linux'" },
+]
+cudart = [
+ { name = "nvidia-cuda-runtime", marker = "sys_platform == 'linux'" },
+]
+cufft = [
+ { name = "nvidia-cufft", marker = "sys_platform == 'linux'" },
+]
+cufile = [
+ { name = "nvidia-cufile", marker = "sys_platform == 'linux'" },
+]
+cupti = [
+ { name = "nvidia-cuda-cupti", marker = "sys_platform == 'linux'" },
+]
+curand = [
+ { name = "nvidia-curand", marker = "sys_platform == 'linux'" },
+]
+cusolver = [
+ { name = "nvidia-cusolver", marker = "sys_platform == 'linux'" },
+]
+cusparse = [
+ { name = "nvidia-cusparse", marker = "sys_platform == 'linux'" },
+]
+nvjitlink = [
+ { name = "nvidia-nvjitlink", marker = "sys_platform == 'linux'" },
+]
+nvrtc = [
+ { name = "nvidia-cuda-nvrtc", marker = "sys_platform == 'linux'" },
+]
+nvtx = [
+ { name = "nvidia-nvtx", marker = "sys_platform == 'linux'" },
+]
+
[[package]]
name = "cycler"
version = "0.12.1"
-source = { registry = "https://pypi.org/simple" }
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615, upload-time = "2023-10-07T05:32:18.335Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321, upload-time = "2023-10-07T05:32:16.783Z" },
@@ -189,11 +260,11 @@ wheels = [
[[package]]
name = "etils"
-version = "1.13.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/9b/a0/522bbff0f3cdd37968f90dd7f26c7aa801ed87f5ba335f156de7f2b88a48/etils-1.13.0.tar.gz", hash = "sha256:a5b60c71f95bcd2d43d4e9fb3dc3879120c1f60472bb5ce19f7a860b1d44f607", size = 106368, upload-time = "2025-07-15T10:29:10.563Z" }
+version = "1.14.0"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/26/ce/6e067242fde898841922ac6fc82b0bb2fe35c38e995880bdffdfbe30182a/etils-1.14.0.tar.gz", hash = "sha256:8136e7f4c4173cd0af0ca5481c4475152f0b8686192951eefa60ee8711e1ede4", size = 108127, upload-time = "2026-03-04T17:41:36.291Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/e7/98/87b5946356095738cb90a6df7b35ff69ac5750f6e783d5fbcc5cb3b6cbd7/etils-1.13.0-py3-none-any.whl", hash = "sha256:d9cd4f40fbe77ad6613b7348a18132cc511237b6c076dbb89105c0b520a4c6bb", size = 170603, upload-time = "2025-07-15T10:29:09.076Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/3d/589663aeeacd59bb2f3e8596bfd3e81cf0fb18d70bb433199041f469771b/etils-1.14.0-py3-none-any.whl", hash = "sha256:b5df7341f54dbe1405a4450b2741207b4a8c279780402b45f87202b94dfc52b4", size = 172934, upload-time = "2026-03-04T17:41:35.01Z" },
]
[package.optional-dependencies]
@@ -203,186 +274,167 @@ epy = [
[[package]]
name = "fastremap"
-version = "1.17.7"
-source = { registry = "https://pypi.org/simple" }
+version = "1.18.0"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
dependencies = [
{ name = "numpy" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/97/c8/d581816df8ee7ab70cd2dd8ee4e60169ceab8062224cc090863e6715f33d/fastremap-1.17.7.tar.gz", hash = "sha256:42776172867d8f2b3348754cf29405ba878af4b06917f12a969514d3097910dc", size = 50034, upload-time = "2025-09-29T23:28:13.031Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/be/7f/98bc1ab6ab9b389a72ed1a97dc34eb57a8e6beb473117c8942481f74e6ca/fastremap-1.17.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4610492ea19f1cc916a05e9195b67de11dc98a18e905de1abf821b2ca2ca1fac", size = 811862, upload-time = "2025-09-29T23:27:24.546Z" },
- { url = "https://files.pythonhosted.org/packages/f7/b6/b88d2a30f50708249bb0414f0581d0f7ccb3785b1a3ca6588565920988f2/fastremap-1.17.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d6130394fc92777d08ca992e70ff6307fe1ef928d2831140ff63ab27f36b6600", size = 655315, upload-time = "2025-09-29T23:27:26.006Z" },
- { url = "https://files.pythonhosted.org/packages/f9/fd/70d7e5ee9b77c3ddbe6d9c479202cf04a0f178c399d94af5993520dab51a/fastremap-1.17.7-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:55f77a4e48fc9614027d318d23399d91a89b62c56d97880055c538fd42c43fd6", size = 7496837, upload-time = "2025-09-29T23:27:27.344Z" },
- { url = "https://files.pythonhosted.org/packages/ff/5a/3ae0f6425c816ac74e130244c152cc5b7d7c13d5c5ff299af074f0456208/fastremap-1.17.7-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:74635d268aa40ef7063319c997c1cbb70d52deeb08a3a61146a6151306c394ea", size = 7621372, upload-time = "2025-09-29T23:27:29.088Z" },
- { url = "https://files.pythonhosted.org/packages/65/9a/193ca90273394cc93d98c9b7a587d134655910e14e12d7813d97d48ed13d/fastremap-1.17.7-cp311-cp311-win32.whl", hash = "sha256:f72d6db9550d9f1308cf78e71ca1bbbedea66048439b0fe688addaedf05c37ff", size = 490649, upload-time = "2025-09-29T23:27:31.719Z" },
- { url = "https://files.pythonhosted.org/packages/71/4b/7a03f72620945f08b40285ff3640e2b0a86f80218c519c8e4c4a557ca645/fastremap-1.17.7-cp311-cp311-win_amd64.whl", hash = "sha256:67cf58fada99981ec1a5b4f3368e1b4c1c4d0f22efaa036748f97475c37ce1f3", size = 685345, upload-time = "2025-09-29T23:27:30.662Z" },
- { url = "https://files.pythonhosted.org/packages/13/87/443b137c927f1c9cea7e4c290d6d49a78b7139382a8abe6cb138a6f11e8c/fastremap-1.17.7-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:682f11e7e8daea113c252938c8d98d28b8cee164121f1f3dcdafd0657b4a065b", size = 784852, upload-time = "2025-09-29T23:27:32.876Z" },
- { url = "https://files.pythonhosted.org/packages/a2/73/6cc98c650cc1b625d52bcc2c41c6b2690c33b678de5c6b0774d4d49cdcee/fastremap-1.17.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:71f370e256a052dabc5cb14a65bb6e070f700ed976db7dc10450014f54e773c3", size = 661224, upload-time = "2025-09-29T23:27:33.903Z" },
- { url = "https://files.pythonhosted.org/packages/10/73/566bed66cb33472fee3b3d3269438b1b026e85a99a6c5252f8e13acc8fbc/fastremap-1.17.7-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a7df1b9ad1659f1820349bb12d8bf76291c4896146d5230ecad5b9c75f2635ab", size = 7272028, upload-time = "2025-09-29T23:27:35.587Z" },
- { url = "https://files.pythonhosted.org/packages/85/be/4c9efaaaa19d0cf5a438fe8055969461d3096d874d3732c36e71ad87a2a0/fastremap-1.17.7-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8bc165a003337c41ed19b0ee20c16c3c8342fcab0726e7072c3c2cf1bf613104", size = 7452828, upload-time = "2025-09-29T23:27:37.339Z" },
- { url = "https://files.pythonhosted.org/packages/35/f0/66d0c8cbdce800c59b60d8257ec77be294b21501bb4d5f94e817ea20f1a7/fastremap-1.17.7-cp312-cp312-win32.whl", hash = "sha256:e278071af4d68a52531efdb861addfaf86e33115e9a53a2703abd3d395ada300", size = 468626, upload-time = "2025-09-29T23:27:40.167Z" },
- { url = "https://files.pythonhosted.org/packages/d6/bf/da8e48bc2c1a89180a739557ba8e15278e2de685a3ce91436c5a5d47cf70/fastremap-1.17.7-cp312-cp312-win_amd64.whl", hash = "sha256:8f10a84cedb56e37627fb0bec570eb5ec9668a1e3c00ac2c93ca13008cc41230", size = 642404, upload-time = "2025-09-29T23:27:38.882Z" },
- { url = "https://files.pythonhosted.org/packages/01/9b/aff83fe7dda6d45ab5d4be8eecfe384761c2575203ee82071ac4bfee8917/fastremap-1.17.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:15a09bc4aa504bf630605ed6fb98b9661c179dbd38aec35436c39a2e42d064d0", size = 783563, upload-time = "2025-09-29T23:27:41.104Z" },
- { url = "https://files.pythonhosted.org/packages/a6/b4/0a4d7c54f2e4e862f4dce47bd5c0be78c59f166b9a7acc0fc86b1d4d20cd/fastremap-1.17.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:189315aaf5c9c5dd38f6f478f5029ed699155b3bed4159c7fc2d8c3d990d91a5", size = 658491, upload-time = "2025-09-29T23:27:42.328Z" },
- { url = "https://files.pythonhosted.org/packages/c1/9b/71c09beb8513c548ce80f19c70584b3e679cd0b60ef8f1dfc17b22063add/fastremap-1.17.7-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2abdf378fa79cdf182375706a07de8df3dacb55ee3f97a28febd464b0e892afe", size = 7140699, upload-time = "2025-09-29T23:27:43.671Z" },
- { url = "https://files.pythonhosted.org/packages/a6/1d/2eeeeae1af1fa5caeaf831c7fa08480f46b9acd475055ec50babb02946fd/fastremap-1.17.7-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:46d44c6c25ca7a8e309d18475be8253ba22350f97f107068e44938f234792f43", size = 7324073, upload-time = "2025-09-29T23:27:45.181Z" },
- { url = "https://files.pythonhosted.org/packages/7f/6c/8f5571ea8ee150a11c0816d00aa4a2564d7bd2ffac1d4c471be8cc54d061/fastremap-1.17.7-cp313-cp313-win32.whl", hash = "sha256:bbcbc4aecb1da7d08469a2306fb9dc08f33695d6be7295385aaee4dd762e2faa", size = 475953, upload-time = "2025-09-29T23:27:47.9Z" },
- { url = "https://files.pythonhosted.org/packages/49/bb/ea8373f6f8836de1a5fa2169660b8d82d95df6faa87e7340818b3a8ff18d/fastremap-1.17.7-cp313-cp313-win_amd64.whl", hash = "sha256:f56e4f02f47865ad86b1d05161bee7fbc88e95a4a18ba3dcc7bbdf66153e4e3c", size = 641894, upload-time = "2025-09-29T23:27:46.498Z" },
- { url = "https://files.pythonhosted.org/packages/b9/c5/779f0dec11a2d2c43839f74198da670b2b84556349656b6e3f5d8ec38924/fastremap-1.17.7-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:0e0cdc4fb04c80fa7b41165a5a25ceb365a32210ba1aa06aa4df8bc120b8c441", size = 789962, upload-time = "2025-09-29T23:27:49.109Z" },
- { url = "https://files.pythonhosted.org/packages/fc/9a/6af0706bea8364344532de92dc01e0a06ab8cfeb4c0321075f0183e08446/fastremap-1.17.7-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8f9161aecea0c8ea7f84efec87f42af0cfca48710d8e4886401db631fbe7a40c", size = 663790, upload-time = "2025-09-29T23:27:50.42Z" },
- { url = "https://files.pythonhosted.org/packages/7f/18/a621d576c6a06840b94c09bc8540f87ab2e269fba3ac7855f570520db43a/fastremap-1.17.7-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:85fabb943477e79059dada3d730731d055ce65cbd7780bff627e4fed88b506a8", size = 7117601, upload-time = "2025-09-29T23:27:52.021Z" },
- { url = "https://files.pythonhosted.org/packages/2e/11/54dabf43a2d62edb380986784d6963cd956978f2c449ef81eaf9eff02da1/fastremap-1.17.7-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:21bc6a09d8025c7da7b44dff34f236d552473ae68c6aa2688d76f0b9b222b300", size = 7218897, upload-time = "2025-09-29T23:27:53.581Z" },
- { url = "https://files.pythonhosted.org/packages/00/6b/e67393a16d4c8596aeb3ec20505fc7f3c5609fb7ef1fcf77515c61599457/fastremap-1.17.7-cp314-cp314-win32.whl", hash = "sha256:7dc1c37c1307f02ce364d694a13815f80f3319849e41383011e7bf35fbd0d53f", size = 479906, upload-time = "2025-09-29T23:27:55.938Z" },
- { url = "https://files.pythonhosted.org/packages/4b/02/70a43c8a76c23dc20f78a1d7041e2077dc7d118799a142183dc84bfa0561/fastremap-1.17.7-cp314-cp314-win_amd64.whl", hash = "sha256:5c4dabeaf0b8e2a5213e86ba23aedfb30583e9d74879fd2195149cd107338917", size = 653262, upload-time = "2025-09-29T23:27:54.825Z" },
- { url = "https://files.pythonhosted.org/packages/06/f6/2778fc7f52b8b98ae401425d4a08f0414d4f8c99357af69704d2220b81f3/fastremap-1.17.7-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:0594239f6e2297150792ba93d7c1fe415e16689bf7df4e80edb897a46c273561", size = 842870, upload-time = "2025-09-29T23:27:56.947Z" },
- { url = "https://files.pythonhosted.org/packages/33/3e/4ff380e1c0f8af9fd6a874f7f594404ea8d811c88f175bf44a7ee166bae4/fastremap-1.17.7-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d79f7376d3ce15fedc9cf594ff5bc1cadbde4a00443a4adbef8adc9b34b10969", size = 731944, upload-time = "2025-09-29T23:27:58.546Z" },
- { url = "https://files.pythonhosted.org/packages/f2/29/c89dd6b6f49e31329d46e177f83375c816f9a7ba31f569685ffbb9294b1b/fastremap-1.17.7-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f6d24c6cdcc0604a9e512ea0440e0705b326286e8457cfba5870a7d590fc85c7", size = 7307438, upload-time = "2025-09-29T23:27:59.859Z" },
- { url = "https://files.pythonhosted.org/packages/70/c0/7cd2e62c4b84410fa9b82b28ac7518ca842871d00e3d8c70ed295d0d8cd0/fastremap-1.17.7-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a5f2205bd4fdf4fa34780aaa4ac7174de9448604cd684ed158dfcbb20105676c", size = 7118873, upload-time = "2025-09-29T23:28:01.804Z" },
- { url = "https://files.pythonhosted.org/packages/5f/14/4953d7585378347f6026ef61947d6bca4c5d3eecbafc19ee0840a3efa003/fastremap-1.17.7-cp314-cp314t-win32.whl", hash = "sha256:30a2d1ac3c75a5668ba19c631098334bf33bb40837cd8c778786a5645bbb0dd4", size = 587944, upload-time = "2025-09-29T23:28:04.037Z" },
- { url = "https://files.pythonhosted.org/packages/27/b1/35a320f03a9556e0aa6091554da89787d48643146b472e5b24971375d852/fastremap-1.17.7-cp314-cp314t-win_amd64.whl", hash = "sha256:eab0c6d093f6dd78ede950fcf4653fe562ce5c741ee1b0f6da19254663ce724b", size = 811544, upload-time = "2025-09-29T23:28:03.084Z" },
+sdist = { url = "https://files.pythonhosted.org/packages/00/b5/c33627109b34520315593c7415cb4e81125114082be07bd4c4e507ab6e17/fastremap-1.18.0.tar.gz", hash = "sha256:8e46acf8ffc7e9733852cc68659b0776ab951bb7e584c6df4f2c14f7bcebea5f", size = 50725, upload-time = "2026-03-17T18:52:28.969Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/42/c4/431d88fe0549508eca857021feea8f1950519a1188bc9b66af18b4751dbc/fastremap-1.18.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0a9d019fc31ac6f429741bcfe787b6783837fd5133743207eb1ed3885a146277", size = 716517, upload-time = "2026-03-17T18:51:43.596Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/42/71ade8de53758f232531a7a3f324c5219ec0136f75b7d47e8ee5a330f2f1/fastremap-1.18.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e19d76fbfed962651be43a021aceea77f07a33140fb61f1ba1febf448dc4c1a5", size = 566581, upload-time = "2026-03-17T18:51:44.943Z" },
+ { url = "https://files.pythonhosted.org/packages/10/c1/863c70a10817d3dae30131f427c325ce8e23e0edf98f95f391f8b432db22/fastremap-1.18.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6a648e39a90fe5aad56b89a90831276c321ef183d9eb6f62cb4fc2e4ebdd107a", size = 6835194, upload-time = "2026-03-17T18:51:47.977Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/36/ab68e1287a29f21cb06dfc07ac2c8d25fbacd63170510c96acd83af1e97f/fastremap-1.18.0-cp311-cp311-win32.whl", hash = "sha256:d23aaf3c795910fd9668dfaf9bca69d2d50815298acc0ca17794ae27d7ee3c96", size = 432502, upload-time = "2026-03-17T18:51:50.942Z" },
+ { url = "https://files.pythonhosted.org/packages/91/17/abc2f751b8e39fdc34aadb27bc25ba73e458eb131f0ed53b1d0c212a86b2/fastremap-1.18.0-cp311-cp311-win_amd64.whl", hash = "sha256:e83f9060c979a26d634056e2541c6d650c8306fb7951aede28495f4985565386", size = 622401, upload-time = "2026-03-17T18:51:49.841Z" },
+ { url = "https://files.pythonhosted.org/packages/83/53/ed1bd08dd6ae711b2b9e7bc73349d8e979526b1be1013ed2cfb8185bb03c/fastremap-1.18.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2b0078af791b9f0cd92e093f0901c226eae9e2b73d09b2cf4f52dd853977130e", size = 679892, upload-time = "2026-03-17T18:51:52.604Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/bd/42883df958b4bdf7c67e58791ade6b67ed63ca831907e7eb039f3da99e55/fastremap-1.18.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:170b70625c0ba8dab77696a5279fbfab06cef1937ba33c5b3e529fbdb0229ae8", size = 565563, upload-time = "2026-03-17T18:51:54.063Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/87/5af5dc3b691f83a1647dfac7899a5e48b245d005c6a87998a71a6c17fa57/fastremap-1.18.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3d56dc77d191b15706ec3266bd7756431b0b90a5bf767b6753e55ab00a545645", size = 6777342, upload-time = "2026-03-17T18:51:56.4Z" },
+ { url = "https://files.pythonhosted.org/packages/52/02/19c85df9129f8e0bfbba43847ff38b644cb58d9891884deed5316bad188e/fastremap-1.18.0-cp312-cp312-win32.whl", hash = "sha256:68b5e5e087d4d75a5db15bd660239aa739a96653a6c49dce092dac0f94e0c356", size = 412274, upload-time = "2026-03-17T18:51:59.323Z" },
+ { url = "https://files.pythonhosted.org/packages/41/d3/385b65b289000c0624bc3afc3098109c2f5b19f440745a9ebaf37a98b0c2/fastremap-1.18.0-cp312-cp312-win_amd64.whl", hash = "sha256:ca2cdaca466f97454d46cce3fea15070b8b28a1e230ec9843d0e92ce4b9a0ef2", size = 578549, upload-time = "2026-03-17T18:51:58.332Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/93/10fac690bd697fe1398435bd59dfef98a58b7c852b6768ed5d0b9bde6681/fastremap-1.18.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1c55207cd905adeaf7a24c38d49afde28b16dd3846a60508b5593f11bb7ef9f9", size = 679327, upload-time = "2026-03-17T18:52:00.433Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/53/46ecb06e02d23e03480d81b24609e1c38ba15eda630c67fdbda62ad5902e/fastremap-1.18.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1b793657d1b7215f5d806c3f92df18d0a1b2de3dc659ec462a0933ed2f862107", size = 564713, upload-time = "2026-03-17T18:52:01.525Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/5c/b7935dccd06916a1fb5bd8bc4e5ac92f89659307b838abb18b8d632410f5/fastremap-1.18.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:38fb274f9ad29cca5b72f4dedb92a196591657ac239e9b29c9951121d1f8862c", size = 6734169, upload-time = "2026-03-17T18:52:03.704Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/0b/49e8afc40de774a373d1404c6103784ea40f6873e163a4c08e426112547b/fastremap-1.18.0-cp313-cp313-win32.whl", hash = "sha256:82717feb7426e2b413b27fad10b22c4c744a4e0f6acb50969338848bb0846f4d", size = 415925, upload-time = "2026-03-17T18:52:06.556Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/c1/19b4a311cf74388a109bf400cb905a81db8a245a8abd8b2aa80de542ff5e/fastremap-1.18.0-cp313-cp313-win_amd64.whl", hash = "sha256:c7f4b442f634942b1fbd6cc820e90cbb70b0f0ba2356ef488378dcbc11f7233e", size = 578688, upload-time = "2026-03-17T18:52:05.098Z" },
+ { url = "https://files.pythonhosted.org/packages/de/51/eee7af4696e848cb589f06b7bf2af573cd22bf7d29969ba4c3fe933614d1/fastremap-1.18.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:534c2148f5853d25e68a5470590aba0d76e9d11b4b34d436b4884db7971c4a86", size = 678321, upload-time = "2026-03-17T18:52:07.858Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/42/5459e5913dda9ec805b695f42b9d9ee91391a66560f83ca8758cd875b32f/fastremap-1.18.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e0623ea36e9465d4fcd6ba82380372242b06d4c23f31a1192d7f2fd2420c615e", size = 573517, upload-time = "2026-03-17T18:52:08.919Z" },
+ { url = "https://files.pythonhosted.org/packages/f7/83/4235a25c069d2fb7775d0e8cb0ecba3654f68dce6f0678301bf5eba0e573/fastremap-1.18.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a528d87f2d8aa4a3dcf3b66b96570706d44e896c202e241e8e31298621b90bbb", size = 6640855, upload-time = "2026-03-17T18:52:11.093Z" },
+ { url = "https://files.pythonhosted.org/packages/61/8b/17090e4fca0b146a640924a6ba680db7534c0db9bf28fb67ddf2d1d3902a/fastremap-1.18.0-cp314-cp314-win32.whl", hash = "sha256:17f99a67dc63fc086f16ff6371dcf8748be3703995fcc047e9452eb5fcca54d9", size = 422083, upload-time = "2026-03-17T18:52:13.696Z" },
+ { url = "https://files.pythonhosted.org/packages/02/d8/f59b750f705f9da030239115d62eb620e7d64eb83100cbecb0ff4c35fc5e/fastremap-1.18.0-cp314-cp314-win_amd64.whl", hash = "sha256:9b120390a80d39ac97c72d62c928b7098ac48960c4e782fe26b153747990c88f", size = 591199, upload-time = "2026-03-17T18:52:12.457Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/c6/01954f9798eaf3fbc24781ed96d5e4e750228764ecc3369aa36a84d8278f/fastremap-1.18.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:34ca03930e01fa8811300e2964ad7f97aff3bf11f57be3fbe5915714b9b856cb", size = 730524, upload-time = "2026-03-17T18:52:14.706Z" },
+ { url = "https://files.pythonhosted.org/packages/66/64/1282cb3dca1ed4209d18d3b64971b7beb5b99dff7d21166098cb766e9c85/fastremap-1.18.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c65418660acdd8bd1f522a7bdd72c2bfb834ef9c754de1fa23989fdfc1cf2a7b", size = 627714, upload-time = "2026-03-17T18:52:16.21Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/d0/267b721686690ce854495a4625bf76ea2ef78b6ccd115038ffd87ddeafa9/fastremap-1.18.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f427bc6731d4b32fb8fb032fa593fd815a049493db366ec9a346a94939d06eb", size = 6514147, upload-time = "2026-03-17T18:52:18.209Z" },
+ { url = "https://files.pythonhosted.org/packages/05/ec/5a14de31d04780c2804d8a629a8ce94e705b475799c9eb3b0e02ca59675d/fastremap-1.18.0-cp314-cp314t-win32.whl", hash = "sha256:87dcaccd0760ec12052ab6962c5783657fd7cd6e1c8cc78efca7bf657a3b18cd", size = 530743, upload-time = "2026-03-17T18:52:20.626Z" },
+ { url = "https://files.pythonhosted.org/packages/22/c5/94b15a4dff7343a0f052b54b2e2e9a105d9b3719b51cf8e58fdeda06098b/fastremap-1.18.0-cp314-cp314t-win_amd64.whl", hash = "sha256:8865b25d157f174d2c17c824dd07bf5017de5824cb26706a2603ead66d95f292", size = 739811, upload-time = "2026-03-17T18:52:19.588Z" },
]
[[package]]
name = "filelock"
-version = "3.20.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/58/46/0028a82567109b5ef6e4d2a1f04a583fb513e6cf9527fcdd09afd817deeb/filelock-3.20.0.tar.gz", hash = "sha256:711e943b4ec6be42e1d4e6690b48dc175c822967466bb31c0c293f34334c13f4", size = 18922, upload-time = "2025-10-08T18:03:50.056Z" }
+version = "3.29.0"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/b5/fe/997687a931ab51049acce6fa1f23e8f01216374ea81374ddee763c493db5/filelock-3.29.0.tar.gz", hash = "sha256:69974355e960702e789734cb4871f884ea6fe50bd8404051a3530bc07809cf90", size = 57571, upload-time = "2026-04-19T15:39:10.068Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/76/91/7216b27286936c16f5b4d0c530087e4a54eead683e6b0b73dd0c64844af6/filelock-3.20.0-py3-none-any.whl", hash = "sha256:339b4732ffda5cd79b13f4e2711a31b0365ce445d95d243bb996273d072546a2", size = 16054, upload-time = "2025-10-08T18:03:48.35Z" },
+ { url = "https://files.pythonhosted.org/packages/81/47/dd9a212ef6e343a6857485ffe25bba537304f1913bdbed446a23f7f592e1/filelock-3.29.0-py3-none-any.whl", hash = "sha256:96f5f6344709aa1572bbf631c640e4ebeeb519e08da902c39a001882f30ac258", size = 39812, upload-time = "2026-04-19T15:39:08.752Z" },
]
[[package]]
name = "fonttools"
-version = "4.60.1"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/4b/42/97a13e47a1e51a5a7142475bbcf5107fe3a68fc34aef331c897d5fb98ad0/fonttools-4.60.1.tar.gz", hash = "sha256:ef00af0439ebfee806b25f24c8f92109157ff3fac5731dc7867957812e87b8d9", size = 3559823, upload-time = "2025-09-29T21:13:27.129Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/ea/85/639aa9bface1537e0fb0f643690672dde0695a5bbbc90736bc571b0b1941/fonttools-4.60.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7b4c32e232a71f63a5d00259ca3d88345ce2a43295bb049d21061f338124246f", size = 2831872, upload-time = "2025-09-29T21:11:20.329Z" },
- { url = "https://files.pythonhosted.org/packages/6b/47/3c63158459c95093be9618794acb1067b3f4d30dcc5c3e8114b70e67a092/fonttools-4.60.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3630e86c484263eaac71d117085d509cbcf7b18f677906824e4bace598fb70d2", size = 2356990, upload-time = "2025-09-29T21:11:22.754Z" },
- { url = "https://files.pythonhosted.org/packages/94/dd/1934b537c86fcf99f9761823f1fc37a98fbd54568e8e613f29a90fed95a9/fonttools-4.60.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5c1015318e4fec75dd4943ad5f6a206d9727adf97410d58b7e32ab644a807914", size = 5042189, upload-time = "2025-09-29T21:11:25.061Z" },
- { url = "https://files.pythonhosted.org/packages/d2/d2/9f4e4c4374dd1daa8367784e1bd910f18ba886db1d6b825b12edf6db3edc/fonttools-4.60.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e6c58beb17380f7c2ea181ea11e7db8c0ceb474c9dd45f48e71e2cb577d146a1", size = 4978683, upload-time = "2025-09-29T21:11:27.693Z" },
- { url = "https://files.pythonhosted.org/packages/cc/c4/0fb2dfd1ecbe9a07954cc13414713ed1eab17b1c0214ef07fc93df234a47/fonttools-4.60.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ec3681a0cb34c255d76dd9d865a55f260164adb9fa02628415cdc2d43ee2c05d", size = 5021372, upload-time = "2025-09-29T21:11:30.257Z" },
- { url = "https://files.pythonhosted.org/packages/0c/d5/495fc7ae2fab20223cc87179a8f50f40f9a6f821f271ba8301ae12bb580f/fonttools-4.60.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f4b5c37a5f40e4d733d3bbaaef082149bee5a5ea3156a785ff64d949bd1353fa", size = 5132562, upload-time = "2025-09-29T21:11:32.737Z" },
- { url = "https://files.pythonhosted.org/packages/bc/fa/021dab618526323c744e0206b3f5c8596a2e7ae9aa38db5948a131123e83/fonttools-4.60.1-cp311-cp311-win32.whl", hash = "sha256:398447f3d8c0c786cbf1209711e79080a40761eb44b27cdafffb48f52bcec258", size = 2230288, upload-time = "2025-09-29T21:11:35.015Z" },
- { url = "https://files.pythonhosted.org/packages/bb/78/0e1a6d22b427579ea5c8273e1c07def2f325b977faaf60bb7ddc01456cb1/fonttools-4.60.1-cp311-cp311-win_amd64.whl", hash = "sha256:d066ea419f719ed87bc2c99a4a4bfd77c2e5949cb724588b9dd58f3fd90b92bf", size = 2278184, upload-time = "2025-09-29T21:11:37.434Z" },
- { url = "https://files.pythonhosted.org/packages/e3/f7/a10b101b7a6f8836a5adb47f2791f2075d044a6ca123f35985c42edc82d8/fonttools-4.60.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:7b0c6d57ab00dae9529f3faf187f2254ea0aa1e04215cf2f1a8ec277c96661bc", size = 2832953, upload-time = "2025-09-29T21:11:39.616Z" },
- { url = "https://files.pythonhosted.org/packages/ed/fe/7bd094b59c926acf2304d2151354ddbeb74b94812f3dc943c231db09cb41/fonttools-4.60.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:839565cbf14645952d933853e8ade66a463684ed6ed6c9345d0faf1f0e868877", size = 2352706, upload-time = "2025-09-29T21:11:41.826Z" },
- { url = "https://files.pythonhosted.org/packages/c0/ca/4bb48a26ed95a1e7eba175535fe5805887682140ee0a0d10a88e1de84208/fonttools-4.60.1-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8177ec9676ea6e1793c8a084a90b65a9f778771998eb919d05db6d4b1c0b114c", size = 4923716, upload-time = "2025-09-29T21:11:43.893Z" },
- { url = "https://files.pythonhosted.org/packages/b8/9f/2cb82999f686c1d1ddf06f6ae1a9117a880adbec113611cc9d22b2fdd465/fonttools-4.60.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:996a4d1834524adbb423385d5a629b868ef9d774670856c63c9a0408a3063401", size = 4968175, upload-time = "2025-09-29T21:11:46.439Z" },
- { url = "https://files.pythonhosted.org/packages/18/79/be569699e37d166b78e6218f2cde8c550204f2505038cdd83b42edc469b9/fonttools-4.60.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a46b2f450bc79e06ef3b6394f0c68660529ed51692606ad7f953fc2e448bc903", size = 4911031, upload-time = "2025-09-29T21:11:48.977Z" },
- { url = "https://files.pythonhosted.org/packages/cc/9f/89411cc116effaec5260ad519162f64f9c150e5522a27cbb05eb62d0c05b/fonttools-4.60.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6ec722ee589e89a89f5b7574f5c45604030aa6ae24cb2c751e2707193b466fed", size = 5062966, upload-time = "2025-09-29T21:11:54.344Z" },
- { url = "https://files.pythonhosted.org/packages/62/a1/f888221934b5731d46cb9991c7a71f30cb1f97c0ef5fcf37f8da8fce6c8e/fonttools-4.60.1-cp312-cp312-win32.whl", hash = "sha256:b2cf105cee600d2de04ca3cfa1f74f1127f8455b71dbad02b9da6ec266e116d6", size = 2218750, upload-time = "2025-09-29T21:11:56.601Z" },
- { url = "https://files.pythonhosted.org/packages/88/8f/a55b5550cd33cd1028601df41acd057d4be20efa5c958f417b0c0613924d/fonttools-4.60.1-cp312-cp312-win_amd64.whl", hash = "sha256:992775c9fbe2cf794786fa0ffca7f09f564ba3499b8fe9f2f80bd7197db60383", size = 2267026, upload-time = "2025-09-29T21:11:58.852Z" },
- { url = "https://files.pythonhosted.org/packages/7c/5b/cdd2c612277b7ac7ec8c0c9bc41812c43dc7b2d5f2b0897e15fdf5a1f915/fonttools-4.60.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6f68576bb4bbf6060c7ab047b1574a1ebe5c50a17de62830079967b211059ebb", size = 2825777, upload-time = "2025-09-29T21:12:01.22Z" },
- { url = "https://files.pythonhosted.org/packages/d6/8a/de9cc0540f542963ba5e8f3a1f6ad48fa211badc3177783b9d5cadf79b5d/fonttools-4.60.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:eedacb5c5d22b7097482fa834bda0dafa3d914a4e829ec83cdea2a01f8c813c4", size = 2348080, upload-time = "2025-09-29T21:12:03.785Z" },
- { url = "https://files.pythonhosted.org/packages/2d/8b/371ab3cec97ee3fe1126b3406b7abd60c8fec8975fd79a3c75cdea0c3d83/fonttools-4.60.1-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b33a7884fabd72bdf5f910d0cf46be50dce86a0362a65cfc746a4168c67eb96c", size = 4903082, upload-time = "2025-09-29T21:12:06.382Z" },
- { url = "https://files.pythonhosted.org/packages/04/05/06b1455e4bc653fcb2117ac3ef5fa3a8a14919b93c60742d04440605d058/fonttools-4.60.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2409d5fb7b55fd70f715e6d34e7a6e4f7511b8ad29a49d6df225ee76da76dd77", size = 4960125, upload-time = "2025-09-29T21:12:09.314Z" },
- { url = "https://files.pythonhosted.org/packages/8e/37/f3b840fcb2666f6cb97038793606bdd83488dca2d0b0fc542ccc20afa668/fonttools-4.60.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c8651e0d4b3bdeda6602b85fdc2abbefc1b41e573ecb37b6779c4ca50753a199", size = 4901454, upload-time = "2025-09-29T21:12:11.931Z" },
- { url = "https://files.pythonhosted.org/packages/fd/9e/eb76f77e82f8d4a46420aadff12cec6237751b0fb9ef1de373186dcffb5f/fonttools-4.60.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:145daa14bf24824b677b9357c5e44fd8895c2a8f53596e1b9ea3496081dc692c", size = 5044495, upload-time = "2025-09-29T21:12:15.241Z" },
- { url = "https://files.pythonhosted.org/packages/f8/b3/cede8f8235d42ff7ae891bae8d619d02c8ac9fd0cfc450c5927a6200c70d/fonttools-4.60.1-cp313-cp313-win32.whl", hash = "sha256:2299df884c11162617a66b7c316957d74a18e3758c0274762d2cc87df7bc0272", size = 2217028, upload-time = "2025-09-29T21:12:17.96Z" },
- { url = "https://files.pythonhosted.org/packages/75/4d/b022c1577807ce8b31ffe055306ec13a866f2337ecee96e75b24b9b753ea/fonttools-4.60.1-cp313-cp313-win_amd64.whl", hash = "sha256:a3db56f153bd4c5c2b619ab02c5db5192e222150ce5a1bc10f16164714bc39ac", size = 2266200, upload-time = "2025-09-29T21:12:20.14Z" },
- { url = "https://files.pythonhosted.org/packages/9a/83/752ca11c1aa9a899b793a130f2e466b79ea0cf7279c8d79c178fc954a07b/fonttools-4.60.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:a884aef09d45ba1206712c7dbda5829562d3fea7726935d3289d343232ecb0d3", size = 2822830, upload-time = "2025-09-29T21:12:24.406Z" },
- { url = "https://files.pythonhosted.org/packages/57/17/bbeab391100331950a96ce55cfbbff27d781c1b85ebafb4167eae50d9fe3/fonttools-4.60.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8a44788d9d91df72d1a5eac49b31aeb887a5f4aab761b4cffc4196c74907ea85", size = 2345524, upload-time = "2025-09-29T21:12:26.819Z" },
- { url = "https://files.pythonhosted.org/packages/3d/2e/d4831caa96d85a84dd0da1d9f90d81cec081f551e0ea216df684092c6c97/fonttools-4.60.1-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e852d9dda9f93ad3651ae1e3bb770eac544ec93c3807888798eccddf84596537", size = 4843490, upload-time = "2025-09-29T21:12:29.123Z" },
- { url = "https://files.pythonhosted.org/packages/49/13/5e2ea7c7a101b6fc3941be65307ef8df92cbbfa6ec4804032baf1893b434/fonttools-4.60.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:154cb6ee417e417bf5f7c42fe25858c9140c26f647c7347c06f0cc2d47eff003", size = 4944184, upload-time = "2025-09-29T21:12:31.414Z" },
- { url = "https://files.pythonhosted.org/packages/0c/2b/cf9603551c525b73fc47c52ee0b82a891579a93d9651ed694e4e2cd08bb8/fonttools-4.60.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:5664fd1a9ea7f244487ac8f10340c4e37664675e8667d6fee420766e0fb3cf08", size = 4890218, upload-time = "2025-09-29T21:12:33.936Z" },
- { url = "https://files.pythonhosted.org/packages/fd/2f/933d2352422e25f2376aae74f79eaa882a50fb3bfef3c0d4f50501267101/fonttools-4.60.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:583b7f8e3c49486e4d489ad1deacfb8d5be54a8ef34d6df824f6a171f8511d99", size = 4999324, upload-time = "2025-09-29T21:12:36.637Z" },
- { url = "https://files.pythonhosted.org/packages/38/99/234594c0391221f66216bc2c886923513b3399a148defaccf81dc3be6560/fonttools-4.60.1-cp314-cp314-win32.whl", hash = "sha256:66929e2ea2810c6533a5184f938502cfdaea4bc3efb7130d8cc02e1c1b4108d6", size = 2220861, upload-time = "2025-09-29T21:12:39.108Z" },
- { url = "https://files.pythonhosted.org/packages/3e/1d/edb5b23726dde50fc4068e1493e4fc7658eeefcaf75d4c5ffce067d07ae5/fonttools-4.60.1-cp314-cp314-win_amd64.whl", hash = "sha256:f3d5be054c461d6a2268831f04091dc82753176f6ea06dc6047a5e168265a987", size = 2270934, upload-time = "2025-09-29T21:12:41.339Z" },
- { url = "https://files.pythonhosted.org/packages/fb/da/1392aaa2170adc7071fe7f9cfd181a5684a7afcde605aebddf1fb4d76df5/fonttools-4.60.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:b6379e7546ba4ae4b18f8ae2b9bc5960936007a1c0e30b342f662577e8bc3299", size = 2894340, upload-time = "2025-09-29T21:12:43.774Z" },
- { url = "https://files.pythonhosted.org/packages/bf/a7/3b9f16e010d536ce567058b931a20b590d8f3177b2eda09edd92e392375d/fonttools-4.60.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9d0ced62b59e0430b3690dbc5373df1c2aa7585e9a8ce38eff87f0fd993c5b01", size = 2375073, upload-time = "2025-09-29T21:12:46.437Z" },
- { url = "https://files.pythonhosted.org/packages/9b/b5/e9bcf51980f98e59bb5bb7c382a63c6f6cac0eec5f67de6d8f2322382065/fonttools-4.60.1-cp314-cp314t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:875cb7764708b3132637f6c5fb385b16eeba0f7ac9fa45a69d35e09b47045801", size = 4849758, upload-time = "2025-09-29T21:12:48.694Z" },
- { url = "https://files.pythonhosted.org/packages/e3/dc/1d2cf7d1cba82264b2f8385db3f5960e3d8ce756b4dc65b700d2c496f7e9/fonttools-4.60.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a184b2ea57b13680ab6d5fbde99ccef152c95c06746cb7718c583abd8f945ccc", size = 5085598, upload-time = "2025-09-29T21:12:51.081Z" },
- { url = "https://files.pythonhosted.org/packages/5d/4d/279e28ba87fb20e0c69baf72b60bbf1c4d873af1476806a7b5f2b7fac1ff/fonttools-4.60.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:026290e4ec76583881763fac284aca67365e0be9f13a7fb137257096114cb3bc", size = 4957603, upload-time = "2025-09-29T21:12:53.423Z" },
- { url = "https://files.pythonhosted.org/packages/78/d4/ff19976305e0c05aa3340c805475abb00224c954d3c65e82c0a69633d55d/fonttools-4.60.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f0e8817c7d1a0c2eedebf57ef9a9896f3ea23324769a9a2061a80fe8852705ed", size = 4974184, upload-time = "2025-09-29T21:12:55.962Z" },
- { url = "https://files.pythonhosted.org/packages/63/22/8553ff6166f5cd21cfaa115aaacaa0dc73b91c079a8cfd54a482cbc0f4f5/fonttools-4.60.1-cp314-cp314t-win32.whl", hash = "sha256:1410155d0e764a4615774e5c2c6fc516259fe3eca5882f034eb9bfdbee056259", size = 2282241, upload-time = "2025-09-29T21:12:58.179Z" },
- { url = "https://files.pythonhosted.org/packages/8a/cb/fa7b4d148e11d5a72761a22e595344133e83a9507a4c231df972e657579b/fonttools-4.60.1-cp314-cp314t-win_amd64.whl", hash = "sha256:022beaea4b73a70295b688f817ddc24ed3e3418b5036ffcd5658141184ef0d0c", size = 2345760, upload-time = "2025-09-29T21:13:00.375Z" },
- { url = "https://files.pythonhosted.org/packages/c7/93/0dd45cd283c32dea1545151d8c3637b4b8c53cdb3a625aeb2885b184d74d/fonttools-4.60.1-py3-none-any.whl", hash = "sha256:906306ac7afe2156fcf0042173d6ebbb05416af70f6b370967b47f8f00103bbb", size = 1143175, upload-time = "2025-09-29T21:13:24.134Z" },
+version = "4.62.1"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/9a/08/7012b00a9a5874311b639c3920270c36ee0c445b69d9989a85e5c92ebcb0/fonttools-4.62.1.tar.gz", hash = "sha256:e54c75fd6041f1122476776880f7c3c3295ffa31962dc6ebe2543c00dca58b5d", size = 3580737, upload-time = "2026-03-13T13:54:25.52Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/88/39/23ff32561ec8d45a4d48578b4d241369d9270dc50926c017570e60893701/fonttools-4.62.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:40975849bac44fb0b9253d77420c6d8b523ac4dcdcefeff6e4d706838a5b80f7", size = 2871039, upload-time = "2026-03-13T13:52:33.127Z" },
+ { url = "https://files.pythonhosted.org/packages/24/7f/66d3f8a9338a9b67fe6e1739f47e1cd5cee78bd3bc1206ef9b0b982289a5/fonttools-4.62.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9dde91633f77fa576879a0c76b1d89de373cae751a98ddf0109d54e173b40f14", size = 2416346, upload-time = "2026-03-13T13:52:35.676Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/53/5276ceba7bff95da7793a07c5284e1da901cf00341ce5e2f3273056c0cca/fonttools-4.62.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6acb4109f8bee00fec985c8c7afb02299e35e9c94b57287f3ea542f28bd0b0a7", size = 5100897, upload-time = "2026-03-13T13:52:38.102Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/a1/40a5c4d8e28b0851d53a8eeeb46fbd73c325a2a9a165f290a5ed90e6c597/fonttools-4.62.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1c5c25671ce8805e0d080e2ffdeca7f1e86778c5cbfbeae86d7f866d8830517b", size = 5071078, upload-time = "2026-03-13T13:52:41.305Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/be/d378fca4c65ea1956fee6d90ace6e861776809cbbc5af22388a090c3c092/fonttools-4.62.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a5d8825e1140f04e6c99bb7d37a9e31c172f3bc208afbe02175339e699c710e1", size = 5076908, upload-time = "2026-03-13T13:52:44.122Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/d9/ae6a1d0693a4185a84605679c8a1f719a55df87b9c6e8e817bfdd9ef5936/fonttools-4.62.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:268abb1cb221e66c014acc234e872b7870d8b5d4657a83a8f4205094c32d2416", size = 5202275, upload-time = "2026-03-13T13:52:46.591Z" },
+ { url = "https://files.pythonhosted.org/packages/54/6c/af95d9c4efb15cabff22642b608342f2bd67137eea6107202d91b5b03184/fonttools-4.62.1-cp311-cp311-win32.whl", hash = "sha256:942b03094d7edbb99bdf1ae7e9090898cad7bf9030b3d21f33d7072dbcb51a53", size = 2293075, upload-time = "2026-03-13T13:52:48.711Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/97/bf54c5b3f2be34e1f143e6db838dfdc54f2ffa3e68c738934c82f3b2a08d/fonttools-4.62.1-cp311-cp311-win_amd64.whl", hash = "sha256:e8514f4924375f77084e81467e63238b095abda5107620f49421c368a6017ed2", size = 2344593, upload-time = "2026-03-13T13:52:50.725Z" },
+ { url = "https://files.pythonhosted.org/packages/47/d4/dbacced3953544b9a93088cc10ef2b596d348c983d5c67a404fa41ec51ba/fonttools-4.62.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:90365821debbd7db678809c7491ca4acd1e0779b9624cdc6ddaf1f31992bf974", size = 2870219, upload-time = "2026-03-13T13:52:53.664Z" },
+ { url = "https://files.pythonhosted.org/packages/66/9e/a769c8e99b81e5a87ab7e5e7236684de4e96246aae17274e5347d11ebd78/fonttools-4.62.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:12859ff0b47dd20f110804c3e0d0970f7b832f561630cd879969011541a464a9", size = 2414891, upload-time = "2026-03-13T13:52:56.493Z" },
+ { url = "https://files.pythonhosted.org/packages/69/64/f19a9e3911968c37e1e620e14dfc5778299e1474f72f4e57c5ec771d9489/fonttools-4.62.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c125ffa00c3d9003cdaaf7f2c79e6e535628093e14b5de1dccb08859b680936", size = 5033197, upload-time = "2026-03-13T13:52:59.179Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/8a/99c8b3c3888c5c474c08dbfd7c8899786de9604b727fcefb055b42c84bba/fonttools-4.62.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:149f7d84afca659d1a97e39a4778794a2f83bf344c5ee5134e09995086cc2392", size = 4988768, upload-time = "2026-03-13T13:53:02.761Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/c6/0f904540d3e6ab463c1243a0d803504826a11604c72dd58c2949796a1762/fonttools-4.62.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0aa72c43a601cfa9273bb1ae0518f1acadc01ee181a6fc60cd758d7fdadffc04", size = 4971512, upload-time = "2026-03-13T13:53:05.678Z" },
+ { url = "https://files.pythonhosted.org/packages/29/0b/5cbef6588dc9bd6b5c9ad6a4d5a8ca384d0cea089da31711bbeb4f9654a6/fonttools-4.62.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:19177c8d96c7c36359266e571c5173bcee9157b59cfc8cb0153c5673dc5a3a7d", size = 5122723, upload-time = "2026-03-13T13:53:08.662Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/47/b3a5342d381595ef439adec67848bed561ab7fdb1019fa522e82101b7d9c/fonttools-4.62.1-cp312-cp312-win32.whl", hash = "sha256:a24decd24d60744ee8b4679d38e88b8303d86772053afc29b19d23bb8207803c", size = 2281278, upload-time = "2026-03-13T13:53:10.998Z" },
+ { url = "https://files.pythonhosted.org/packages/28/b1/0c2ab56a16f409c6c8a68816e6af707827ad5d629634691ff60a52879792/fonttools-4.62.1-cp312-cp312-win_amd64.whl", hash = "sha256:9e7863e10b3de72376280b515d35b14f5eeed639d1aa7824f4cf06779ec65e42", size = 2331414, upload-time = "2026-03-13T13:53:13.992Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/56/6f389de21c49555553d6a5aeed5ac9767631497ac836c4f076273d15bd72/fonttools-4.62.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c22b1014017111c401469e3acc5433e6acf6ebcc6aa9efb538a533c800971c79", size = 2865155, upload-time = "2026-03-13T13:53:16.132Z" },
+ { url = "https://files.pythonhosted.org/packages/03/c5/0e3966edd5ec668d41dfe418787726752bc07e2f5fd8c8f208615e61fa89/fonttools-4.62.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:68959f5fc58ed4599b44aad161c2837477d7f35f5f79402d97439974faebfebe", size = 2412802, upload-time = "2026-03-13T13:53:18.878Z" },
+ { url = "https://files.pythonhosted.org/packages/52/94/e6ac4b44026de7786fe46e3bfa0c87e51d5d70a841054065d49cd62bb909/fonttools-4.62.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef46db46c9447103b8f3ff91e8ba009d5fe181b1920a83757a5762551e32bb68", size = 5013926, upload-time = "2026-03-13T13:53:21.379Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/98/8b1e801939839d405f1f122e7d175cebe9aeb4e114f95bfc45e3152af9a7/fonttools-4.62.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6706d1cb1d5e6251a97ad3c1b9347505c5615c112e66047abbef0f8545fa30d1", size = 4964575, upload-time = "2026-03-13T13:53:23.857Z" },
+ { url = "https://files.pythonhosted.org/packages/46/76/7d051671e938b1881670528fec69cc4044315edd71a229c7fd712eaa5119/fonttools-4.62.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2e7abd2b1e11736f58c1de27819e1955a53267c21732e78243fa2fa2e5c1e069", size = 4953693, upload-time = "2026-03-13T13:53:26.569Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/ae/b41f8628ec0be3c1b934fc12b84f4576a5c646119db4d3bdd76a217c90b5/fonttools-4.62.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:403d28ce06ebfc547fbcb0cb8b7f7cc2f7a2d3e1a67ba9a34b14632df9e080f9", size = 5094920, upload-time = "2026-03-13T13:53:29.329Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/f6/53a1e9469331a23dcc400970a27a4caa3d9f6edbf5baab0260285238b884/fonttools-4.62.1-cp313-cp313-win32.whl", hash = "sha256:93c316e0f5301b2adbe6a5f658634307c096fd5aae60a5b3412e4f3e1728ab24", size = 2279928, upload-time = "2026-03-13T13:53:32.352Z" },
+ { url = "https://files.pythonhosted.org/packages/38/60/35186529de1db3c01f5ad625bde07c1f576305eab6d86bbda4c58445f721/fonttools-4.62.1-cp313-cp313-win_amd64.whl", hash = "sha256:7aa21ff53e28a9c2157acbc44e5b401149d3c9178107130e82d74ceb500e5056", size = 2330514, upload-time = "2026-03-13T13:53:34.991Z" },
+ { url = "https://files.pythonhosted.org/packages/36/f0/2888cdac391807d68d90dcb16ef858ddc1b5309bfc6966195a459dd326e2/fonttools-4.62.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:fa1d16210b6b10a826d71bed68dd9ec24a9e218d5a5e2797f37c573e7ec215ca", size = 2864442, upload-time = "2026-03-13T13:53:37.509Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/b2/e521803081f8dc35990816b82da6360fa668a21b44da4b53fc9e77efcd62/fonttools-4.62.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:aa69d10ed420d8121118e628ad47d86e4caa79ba37f968597b958f6cceab7eca", size = 2410901, upload-time = "2026-03-13T13:53:40.55Z" },
+ { url = "https://files.pythonhosted.org/packages/00/a4/8c3511ff06e53110039358dbbdc1a65d72157a054638387aa2ada300a8b8/fonttools-4.62.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd13b7999d59c5eb1c2b442eb2d0c427cb517a0b7a1f5798fc5c9e003f5ff782", size = 4999608, upload-time = "2026-03-13T13:53:42.798Z" },
+ { url = "https://files.pythonhosted.org/packages/28/63/cd0c3b26afe60995a5295f37c246a93d454023726c3261cfbb3559969bb9/fonttools-4.62.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8d337fdd49a79b0d51c4da87bc38169d21c3abbf0c1aa9367eff5c6656fb6dae", size = 4912726, upload-time = "2026-03-13T13:53:45.405Z" },
+ { url = "https://files.pythonhosted.org/packages/70/b9/ac677cb07c24c685cf34f64e140617d58789d67a3dd524164b63648c6114/fonttools-4.62.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d241cdc4a67b5431c6d7f115fdf63335222414995e3a1df1a41e1182acd4bcc7", size = 4951422, upload-time = "2026-03-13T13:53:48.326Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/10/11c08419a14b85b7ca9a9faca321accccc8842dd9e0b1c8a72908de05945/fonttools-4.62.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c05557a78f8fa514da0f869556eeda40887a8abc77c76ee3f74cf241778afd5a", size = 5060979, upload-time = "2026-03-13T13:53:51.366Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/3c/12eea4a4cf054e7ab058ed5ceada43b46809fce2bf319017c4d63ae55bb4/fonttools-4.62.1-cp314-cp314-win32.whl", hash = "sha256:49a445d2f544ce4a69338694cad575ba97b9a75fff02720da0882d1a73f12800", size = 2283733, upload-time = "2026-03-13T13:53:53.606Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/67/74b070029043186b5dd13462c958cb7c7f811be0d2e634309d9a1ffb1505/fonttools-4.62.1-cp314-cp314-win_amd64.whl", hash = "sha256:1eecc128c86c552fb963fe846ca4e011b1be053728f798185a1687502f6d398e", size = 2335663, upload-time = "2026-03-13T13:53:56.23Z" },
+ { url = "https://files.pythonhosted.org/packages/42/c5/4d2ed3ca6e33617fc5624467da353337f06e7f637707478903c785bd8e20/fonttools-4.62.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:1596aeaddf7f78e21e68293c011316a25267b3effdaccaf4d59bc9159d681b82", size = 2947288, upload-time = "2026-03-13T13:53:59.397Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/e9/7ab11ddfda48ed0f89b13380e5595ba572619c27077be0b2c447a63ff351/fonttools-4.62.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:8f8fca95d3bb3208f59626a4b0ea6e526ee51f5a8ad5d91821c165903e8d9260", size = 2449023, upload-time = "2026-03-13T13:54:01.642Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/10/a800fa090b5e8819942e54e19b55fc7c21fe14a08757c3aa3ca8db358939/fonttools-4.62.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee91628c08e76f77b533d65feb3fbe6d9dad699f95be51cf0d022db94089cdc4", size = 5137599, upload-time = "2026-03-13T13:54:04.495Z" },
+ { url = "https://files.pythonhosted.org/packages/37/dc/8ccd45033fffd74deb6912fa1ca524643f584b94c87a16036855b498a1ed/fonttools-4.62.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5f37df1cac61d906e7b836abe356bc2f34c99d4477467755c216b72aa3dc748b", size = 4920933, upload-time = "2026-03-13T13:54:07.557Z" },
+ { url = "https://files.pythonhosted.org/packages/99/eb/e618adefb839598d25ac8136cd577925d6c513dc0d931d93b8af956210f0/fonttools-4.62.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:92bb00a947e666169c99b43753c4305fc95a890a60ef3aeb2a6963e07902cc87", size = 5016232, upload-time = "2026-03-13T13:54:10.611Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/5f/9b5c9bfaa8ec82def8d8168c4f13615990d6ce5996fe52bd49bfb5e05134/fonttools-4.62.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:bdfe592802ef939a0e33106ea4a318eeb17822c7ee168c290273cbd5fabd746c", size = 5042987, upload-time = "2026-03-13T13:54:13.569Z" },
+ { url = "https://files.pythonhosted.org/packages/90/aa/dfbbe24c6a6afc5c203d90cc0343e24bcbb09e76d67c4d6eef8c2558d7ba/fonttools-4.62.1-cp314-cp314t-win32.whl", hash = "sha256:b820fcb92d4655513d8402d5b219f94481c4443d825b4372c75a2072aa4b357a", size = 2348021, upload-time = "2026-03-13T13:54:16.98Z" },
+ { url = "https://files.pythonhosted.org/packages/13/6f/ae9c4e4dd417948407b680855c2c7790efb52add6009aaecff1e3bc50e8e/fonttools-4.62.1-cp314-cp314t-win_amd64.whl", hash = "sha256:59b372b4f0e113d3746b88985f1c796e7bf830dd54b28374cd85c2b8acd7583e", size = 2414147, upload-time = "2026-03-13T13:54:19.416Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/ba/56147c165442cc5ba7e82ecf301c9a68353cede498185869e6e02b4c264f/fonttools-4.62.1-py3-none-any.whl", hash = "sha256:7487782e2113861f4ddcc07c3436450659e3caa5e470b27dc2177cade2d8e7fd", size = 1152647, upload-time = "2026-03-13T13:54:22.735Z" },
]
[[package]]
name = "fsspec"
-version = "2025.9.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/de/e0/bab50af11c2d75c9c4a2a26a5254573c0bd97cea152254401510950486fa/fsspec-2025.9.0.tar.gz", hash = "sha256:19fd429483d25d28b65ec68f9f4adc16c17ea2c7c7bf54ec61360d478fb19c19", size = 304847, upload-time = "2025-09-02T19:10:49.215Z" }
+version = "2026.3.0"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/e1/cf/b50ddf667c15276a9ab15a70ef5f257564de271957933ffea49d2cdbcdfb/fsspec-2026.3.0.tar.gz", hash = "sha256:1ee6a0e28677557f8c2f994e3eea77db6392b4de9cd1f5d7a9e87a0ae9d01b41", size = 313547, upload-time = "2026-03-27T19:11:14.892Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/47/71/70db47e4f6ce3e5c37a607355f80da8860a33226be640226ac52cb05ef2e/fsspec-2025.9.0-py3-none-any.whl", hash = "sha256:530dc2a2af60a414a832059574df4a6e10cce927f6f4a78209390fe38955cfb7", size = 199289, upload-time = "2025-09-02T19:10:47.708Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/1f/5f4a3cd9e4440e9d9bc78ad0a91a1c8d46b4d429d5239ebe6793c9fe5c41/fsspec-2026.3.0-py3-none-any.whl", hash = "sha256:d2ceafaad1b3457968ed14efa28798162f1638dbb5d2a6868a2db002a5ee39a4", size = 202595, upload-time = "2026-03-27T19:11:13.595Z" },
]
[[package]]
name = "imagecodecs"
-version = "2025.8.2"
-source = { registry = "https://pypi.org/simple" }
+version = "2026.1.14"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
dependencies = [
{ name = "numpy" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/6b/d1/bac78c9bdecbc12bcaa96363dd6d7b358de0b4af512d08c16f6792e7a6ea/imagecodecs-2025.8.2.tar.gz", hash = "sha256:2af272aac90c370326a7e2fffcbbbd32d42de07576959a2a98d60110267dfe6c", size = 9490135, upload-time = "2025-08-03T06:08:38.148Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/3f/af/85a9fbfd3404d9e7050052907086a0d78004b953716e104c6c67afe01a7a/imagecodecs-2025.8.2-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:be6524a5aaaa22684390d7450b647e4c2ffcf284eb2433ca344518292eb484ba", size = 12518594, upload-time = "2025-08-03T06:06:37.981Z" },
- { url = "https://files.pythonhosted.org/packages/e1/b2/ad5c38b500a6bc12762c0a16529a8123abfa12b23fb937e74357ffad9bca/imagecodecs-2025.8.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d588e51fe0ff35d759c310e209c12c76f5db35893de3e95c8e7fa69d78244ca5", size = 10168537, upload-time = "2025-08-03T06:06:40.788Z" },
- { url = "https://files.pythonhosted.org/packages/3c/8e/58a58f851043325f955bfc30eef14a23bc15dd02c343f7bf4028c27fd005/imagecodecs-2025.8.2-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e13144b6af43bf75024a92f1522af6d8fd0ef61c6e0fcd01ae028961df93963a", size = 25522552, upload-time = "2025-08-03T06:06:44.527Z" },
- { url = "https://files.pythonhosted.org/packages/d6/7e/4eb0dc145b7abfbff4139251326946b44d375ae0ab29c775a58adee63a0f/imagecodecs-2025.8.2-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e993b0b1601d19ee1aadb10fceb93524d88b874ee728f82d3f9b40bddccd223", size = 26369822, upload-time = "2025-08-03T06:06:48.308Z" },
- { url = "https://files.pythonhosted.org/packages/ee/1b/05baebc43671f7f6d1d169841438cd689a12e257a021eab6fa5ebc00b5e1/imagecodecs-2025.8.2-cp311-cp311-win32.whl", hash = "sha256:a1ec2006cbc25bf273616c90478338d44f37aafa50935dcbc18767775b416722", size = 18567263, upload-time = "2025-08-03T06:06:51.692Z" },
- { url = "https://files.pythonhosted.org/packages/10/27/0254f64a1d759f22f2c3add691ef915bba016aa7b3630192bbaa9ce1d65d/imagecodecs-2025.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:369a2e0784787ec2ea56676de5fdb0bdaff3202679f45b27e51261f0960923e1", size = 22686127, upload-time = "2025-08-03T06:06:55.182Z" },
- { url = "https://files.pythonhosted.org/packages/72/89/3a43d23e27da6e145148b96886b9f1b32b864cc4dc5f5356e585e2eef863/imagecodecs-2025.8.2-cp311-cp311-win_arm64.whl", hash = "sha256:5fa688d3521edeb192179c9808182f00064fb123af11c989a5d509e36a178ebe", size = 18003465, upload-time = "2025-08-03T06:06:58.387Z" },
- { url = "https://files.pythonhosted.org/packages/3b/2e/b129b2bf95a4332b86452dd93829cb6ab1e203d86a488fc8639e9f08db7d/imagecodecs-2025.8.2-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:32ce9707c85cf90e5fb4d9e076ec78288f0f1c084954376f798397743bc4d4e5", size = 12480416, upload-time = "2025-08-03T06:07:02.511Z" },
- { url = "https://files.pythonhosted.org/packages/dc/36/b1d3117c34f5eebfd7a92314042338dce608654173c71b1fb707e416aef5/imagecodecs-2025.8.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7b7cd0c41ea83f249db2f4ee04c7c3f23bd6fbef89a58bdaa7d2bf11421ffc15", size = 10127356, upload-time = "2025-08-03T06:07:04.771Z" },
- { url = "https://files.pythonhosted.org/packages/e6/41/9ee8f13c4e3425c1aed0b9c5bca5547b968da20cb60af094fafcb52be05f/imagecodecs-2025.8.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10f421048206ffb6763b1eb28f2205535801794e9f2c612bbcc219d14b9e44bc", size = 25765528, upload-time = "2025-08-03T06:07:08.044Z" },
- { url = "https://files.pythonhosted.org/packages/d6/1a/f12736807a179a4bd5a26387f2edf00cc3f972139b61f2833f6fbe9b9685/imagecodecs-2025.8.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f14a5eb8151e32316028c09212eed1d14334460e8adbbef819c1d8ec6ab7e633", size = 26718026, upload-time = "2025-08-03T06:07:13.54Z" },
- { url = "https://files.pythonhosted.org/packages/9c/cd/b0367017443af901279952817900e7f496998fb11de24d91cccdb4fa3dc3/imagecodecs-2025.8.2-cp312-cp312-win32.whl", hash = "sha256:e5cb491fa698d055643d3519bab36a3da8d4144ac759430464e2f75f945f3325", size = 18546766, upload-time = "2025-08-03T06:07:16.836Z" },
- { url = "https://files.pythonhosted.org/packages/c4/97/5eb48564f5fdbe3ef83a07ba45d301d45bf85af382a051de34e0a3d6af85/imagecodecs-2025.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:7371169cff1b98f07a983b28fffd2d334cf40a61fce3c876dda930149081bdeb", size = 22674367, upload-time = "2025-08-03T06:07:20.208Z" },
- { url = "https://files.pythonhosted.org/packages/2b/b9/a1a70b5d2250937cd747ceff94ddb9cb70d6f1b9fef9f4164d88c63dd3cb/imagecodecs-2025.8.2-cp312-cp312-win_arm64.whl", hash = "sha256:bf0a97cd58810d5d7ecd983332f1f85a0df991080a774f7caadb1add172165d0", size = 17981844, upload-time = "2025-08-03T06:07:23.219Z" },
- { url = "https://files.pythonhosted.org/packages/f2/69/77a5c020030dbba1a566264b9201f091534b4b10e9ac5725b7bd40895a8b/imagecodecs-2025.8.2-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:06a1b54847cbf595ed58879936eabdab990f1780b80d03a8d4edb57e61c768b0", size = 12454348, upload-time = "2025-08-03T06:07:26.135Z" },
- { url = "https://files.pythonhosted.org/packages/f9/5d/c5dd7f0706dc48d38deefe4cba05ac78236b662a8ef86f9c0cc569b9db96/imagecodecs-2025.8.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dbf3abd281034e29034b773407c27eff35dd94832a9b6e3c97490db079e3c0a8", size = 10103114, upload-time = "2025-08-03T06:07:28.657Z" },
- { url = "https://files.pythonhosted.org/packages/60/40/8b656577120f758ce4b177917b57c76f15e695ff0e63584f641db2063bbe/imagecodecs-2025.8.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:248bb2ea3e43690196acdb1dae5aa40213dbd00c47255295d57da80770ceeaa7", size = 25602557, upload-time = "2025-08-03T06:07:32.399Z" },
- { url = "https://files.pythonhosted.org/packages/8a/de/6c1cf78cc0ecc45d98a0eb0d8920df7b90719f8643c7ed9b1bb700f95890/imagecodecs-2025.8.2-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:52230fcd0c331b0167ccd14d7ce764bb780006b65bf69761d8bde6863419fdbf", size = 26544468, upload-time = "2025-08-03T06:07:36.096Z" },
- { url = "https://files.pythonhosted.org/packages/51/14/bda4974256e31eca65e33446026c91d54d1124aa59ce042fc326836597ec/imagecodecs-2025.8.2-cp313-cp313-win32.whl", hash = "sha256:151f9e0879ed8a025b19fcffbf4785dacf29002d5fec94d318e84ba154ddd54c", size = 18534480, upload-time = "2025-08-03T06:07:39.901Z" },
- { url = "https://files.pythonhosted.org/packages/a6/bb/ada8851ee56ab835562fb96f764f055e16a5d43a81ebc95207f6b2f3c1d4/imagecodecs-2025.8.2-cp313-cp313-win_amd64.whl", hash = "sha256:d167c265caaf36f9f090e55b68a7c0ec4e5b436923cdc047358743f450534154", size = 22662134, upload-time = "2025-08-03T06:07:43.845Z" },
- { url = "https://files.pythonhosted.org/packages/7c/b6/687ad4e137fe637213540cf71bf6a3957cc48667ce6d96d6d9dcb8409305/imagecodecs-2025.8.2-cp313-cp313-win_arm64.whl", hash = "sha256:37199e19d61c7a6c0cf901859d7a81c4449d18047a15ca9d2ebe17176c8d1b69", size = 17968181, upload-time = "2025-08-03T06:07:47.254Z" },
- { url = "https://files.pythonhosted.org/packages/63/5f/2be51d6ea6e6cae13d8d4ce77d5076ef72e492f670368bb193db35e146ce/imagecodecs-2025.8.2-cp314-cp314-macosx_10_14_x86_64.whl", hash = "sha256:7a3c89b5f5c946d5649892e15b5c89aeca357d048331c3a4ae89009320d2704a", size = 12447596, upload-time = "2025-08-03T06:07:49.804Z" },
- { url = "https://files.pythonhosted.org/packages/6e/75/d9cd579b1fd5fdef5742567236adb49df15beaf8f66219412f21fb86a64c/imagecodecs-2025.8.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3a4d2249e4d25a57da7da336c68efc72a7e15f9271dc6c13c322947f30383b8e", size = 10107274, upload-time = "2025-08-03T06:07:52.787Z" },
- { url = "https://files.pythonhosted.org/packages/57/46/011e41f99f2301091f902afd917a4a8079056510dfcb8b8529d96e4232c8/imagecodecs-2025.8.2-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:31ebfca1bd01c6e4db25c0a91301ddc9aeca1dc63642db689543a9700a5869e8", size = 25579702, upload-time = "2025-08-03T06:07:56.392Z" },
- { url = "https://files.pythonhosted.org/packages/bd/9b/b2d38a3902b2a61cfdbe1bca7aa939b77e32349d00aba7a4014d1dfd8cb9/imagecodecs-2025.8.2-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:be84bd8f3cf1552efc92f2465af5ab40a14a788d48482ef3a702e9dae5f4cd0b", size = 26372160, upload-time = "2025-08-03T06:08:00.079Z" },
- { url = "https://files.pythonhosted.org/packages/c2/96/a22ce5b43a93d12b21f2ec9e374cd4a7edf9347630c7ed90bef7c4ca9f5d/imagecodecs-2025.8.2-cp314-cp314-win32.whl", hash = "sha256:ce57af27547c42dfb888562a1a22dc51a6103c20b3fb69ac4c26121acc741ade", size = 18802212, upload-time = "2025-08-03T06:08:03.341Z" },
- { url = "https://files.pythonhosted.org/packages/b3/0c/d364bff2ffb8f2864e9f7fa6dd05b57c25a4c341ba1e2f08b3dea1dc8a6d/imagecodecs-2025.8.2-cp314-cp314-win_amd64.whl", hash = "sha256:b93d77293c0aa9e661d42f3203b13ea135d5bf9f0936fbbe90780ed1c67322d3", size = 23052776, upload-time = "2025-08-03T06:08:06.975Z" },
- { url = "https://files.pythonhosted.org/packages/7d/4b/54d1e3a2b9d11c8a875c98b543dc5527c259c174b05987b1e79ccfc107b9/imagecodecs-2025.8.2-cp314-cp314-win_arm64.whl", hash = "sha256:d7a53983d4df035761dc652e44c912092bddc5b115d6f5f612df301ce94fcd55", size = 18374009, upload-time = "2025-08-03T06:08:10.074Z" },
+sdist = { url = "https://files.pythonhosted.org/packages/af/75/cedaa3dba300df85712515b9c9e13d848ea9557b796b6c44b50bd361571e/imagecodecs-2026.1.14.tar.gz", hash = "sha256:e37ef5116d41ba90b1c9d1d7121846671fd65c271f0c15ef24208353fa79b283", size = 9527808, upload-time = "2026-01-14T04:24:31.234Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f6/b2/c36b8633c3303ed8d80c8d8490c8488baa7f9e6f46fd11688cb9e68eae5e/imagecodecs-2026.1.14-cp311-abi3-macosx_10_14_x86_64.whl", hash = "sha256:b94c57922816eb025d443f4594f1235d80f0f56b4b48aa9b60bf9d679ea49415", size = 12861649, upload-time = "2026-01-14T04:23:45.235Z" },
+ { url = "https://files.pythonhosted.org/packages/26/79/71780373cef2ec5d9d2f111010ff0a16de0788fdc9a8f26f3cce05d0ed38/imagecodecs-2026.1.14-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:bd72bdff628d6c32c71f086e488f483abbf84816b05d439964d980af49f2a9c5", size = 10700009, upload-time = "2026-01-14T04:23:48.108Z" },
+ { url = "https://files.pythonhosted.org/packages/84/b4/48fc1a9b2379941a752d046b6d9217a1e82c09ed11184a18245cbb0d9c8b/imagecodecs-2026.1.14-cp311-abi3-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e92f8b3bddf632c23d3a832f35e5a2c2326eb0e2ae1ebce419789cce63e5c30", size = 23748325, upload-time = "2026-01-14T04:23:51.644Z" },
+ { url = "https://files.pythonhosted.org/packages/02/e1/8a299b91a4abee7c299c0c6625f9c0985c623fd4b6b41b5a5fe92508bb18/imagecodecs-2026.1.14-cp311-abi3-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8a78451926905459f42e827c207d80e1601abf68eaf0e38fd65ddd3c346d1f47", size = 24716561, upload-time = "2026-01-14T04:23:55.275Z" },
+ { url = "https://files.pythonhosted.org/packages/81/af/f03fa2a60617a46814fea5523f8d53a703aabbb316e029b21efe8fe04f9f/imagecodecs-2026.1.14-cp311-abi3-win32.whl", hash = "sha256:6bbf7defac9f71e0401305440f7e94160201789e03ee75e6e5709bc904742429", size = 17224233, upload-time = "2026-01-14T04:23:58.261Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/22/ae01473653dbad9e10a1632b5d8ae6473de0f3ec2b82c912836661d501a9/imagecodecs-2026.1.14-cp311-abi3-win_amd64.whl", hash = "sha256:13ec4659d05010aa072644f100d0e1e1fcc61d7eaa960923b8216272682e6c9a", size = 21544843, upload-time = "2026-01-14T04:24:02.387Z" },
+ { url = "https://files.pythonhosted.org/packages/54/6e/86fa1a07aee2ea39acfa04e372a144a72b4cdc80f40a11a3ee312c12d312/imagecodecs-2026.1.14-cp311-abi3-win_arm64.whl", hash = "sha256:2a6102f3b99c66e090a619f8a0204e6e95c01399854ffa09e4a9de476dc1671a", size = 16893409, upload-time = "2026-01-14T04:24:05.764Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/a3/f92989c2de762ee4db9f5454bbafa540cfa13d28006b4aeb01936f9a4a8a/imagecodecs-2026.1.14-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:455915b32ae1ecccaeb9dad32ad1499aa2e6928b6b853e3635cb32af9aa56857", size = 13202548, upload-time = "2026-01-14T04:24:08.515Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/da/9fb0e446f5af6fa21f44a1f1d4950e538345d7e8931b5983a543a24b9c38/imagecodecs-2026.1.14-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c189f5d842f1f2524c27c307220c5f2cf201af44dab74b1f7476645f72eb2e87", size = 11004937, upload-time = "2026-01-14T04:24:11.054Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/e9/0400722f5bdaaa9bc13ad77c78606cfb767f2b609cb136cc81d81c380807/imagecodecs-2026.1.14-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ca3325e68dffeb29383f4a28957ac61d3c0ade1b244ebf70797bdaf009ad1578", size = 27442756, upload-time = "2026-01-14T04:24:15.247Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/f1/f39cacf55d5e7175f1e7b5222178e7c56d6ca1a49e61011adb1150d85aec/imagecodecs-2026.1.14-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a6819b8365cf28e2a85dcb04fb9c1f1acb63cf5da61495910cd751a7e2548e1d", size = 28006255, upload-time = "2026-01-14T04:24:19.458Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/58/b098490e93435ef4311c473e30588e80f966f6d676f1343804f8ab3fcff1/imagecodecs-2026.1.14-cp314-cp314t-win32.whl", hash = "sha256:ae422def59fab163ba0cb79470e10aa64b8a62b29e74d50ecd0edab4349444c2", size = 17909361, upload-time = "2026-01-14T04:24:22.548Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/35/cb1fc0ce35dd61475672af65607fe2c3cc30d7844d617067a3e649b8366e/imagecodecs-2026.1.14-cp314-cp314t-win_amd64.whl", hash = "sha256:9b11ced125e7b8da2a9d1a30505827cdb27f7023ccee4cd4cc5e3b352dce8574", size = 22496362, upload-time = "2026-01-14T04:24:25.874Z" },
+ { url = "https://files.pythonhosted.org/packages/04/f7/2dfe80928bf0c6c6e7411954fa008a71d0b08e72fe1028fe9a8a8116588a/imagecodecs-2026.1.14-cp314-cp314t-win_arm64.whl", hash = "sha256:d4c19df6142481d2fda62d91542c078aecd647f120da52cc79019ed6938e587d", size = 17534288, upload-time = "2026-01-14T04:24:28.911Z" },
]
[[package]]
name = "imageio"
-version = "2.37.0"
-source = { registry = "https://pypi.org/simple" }
+version = "2.37.3"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
dependencies = [
{ name = "numpy" },
{ name = "pillow" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/0c/47/57e897fb7094afb2d26e8b2e4af9a45c7cf1a405acdeeca001fdf2c98501/imageio-2.37.0.tar.gz", hash = "sha256:71b57b3669666272c818497aebba2b4c5f20d5b37c81720e5e1a56d59c492996", size = 389963, upload-time = "2025-01-20T02:42:37.089Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/b1/84/93bcd1300216ea50811cee96873b84a1bebf8d0489ffaf7f2a3756bab866/imageio-2.37.3.tar.gz", hash = "sha256:bbb37efbfc4c400fcd534b367b91fcd66d5da639aaa138034431a1c5e0a41451", size = 389673, upload-time = "2026-03-09T11:31:12.573Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/cb/bd/b394387b598ed84d8d0fa90611a90bee0adc2021820ad5729f7ced74a8e2/imageio-2.37.0-py3-none-any.whl", hash = "sha256:11efa15b87bc7871b61590326b2d635439acc321cf7f8ce996f812543ce10eed", size = 315796, upload-time = "2025-01-20T02:42:34.931Z" },
+ { url = "https://files.pythonhosted.org/packages/49/fa/391e437a34e55095173dca5f24070d89cbc233ff85bf1c29c93248c6588d/imageio-2.37.3-py3-none-any.whl", hash = "sha256:46f5bb8522cd421c0f5ae104d8268f569d856b29eb1a13b92829d1970f32c9f0", size = 317646, upload-time = "2026-03-09T11:31:10.771Z" },
]
[[package]]
name = "importlib-resources"
-version = "6.5.2"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/cf/8c/f834fbf984f691b4f7ff60f50b514cc3de5cc08abfc3295564dd89c5e2e7/importlib_resources-6.5.2.tar.gz", hash = "sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c", size = 44693, upload-time = "2025-01-03T18:51:56.698Z" }
+version = "7.1.0"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/e4/06/b56dfa750b44e86157093bc8fca0ab81dccbf5260510de4eaf1cb69b5b99/importlib_resources-7.1.0.tar.gz", hash = "sha256:0722d4c6212489c530f2a145a34c0a7a3b4721bc96a15fada5930e2a0b760708", size = 44985, upload-time = "2026-04-12T16:36:09.232Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/a4/ed/1f1afb2e9e7f38a545d628f864d562a5ae64fe6f7a10e28ffb9b185b4e89/importlib_resources-6.5.2-py3-none-any.whl", hash = "sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec", size = 37461, upload-time = "2025-01-03T18:51:54.306Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/db/55a262f3606bebcae07cc14095338471ad7c0bbcaa37707e6f0ee49725b7/importlib_resources-7.1.0-py3-none-any.whl", hash = "sha256:1bd7b48b4088eddb2cd16382150bb515af0bd2c70128194392725f82ad2c96a1", size = 37232, upload-time = "2026-04-12T16:36:08.219Z" },
]
[[package]]
name = "iniconfig"
-version = "2.1.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" }
+version = "2.3.0"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" },
]
[[package]]
name = "jax"
-version = "0.4.34"
-source = { registry = "https://pypi.org/simple" }
+version = "0.4.35"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
dependencies = [
{ name = "jaxlib" },
{ name = "ml-dtypes" },
@@ -390,15 +442,15 @@ dependencies = [
{ name = "opt-einsum" },
{ name = "scipy" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/19/6a/cacfcdf77841a4562e555ef35e0dbc5f8ca79c9f1010aaa4cf3973e79c69/jax-0.4.34.tar.gz", hash = "sha256:44196854f40c5f9cea3142824b9f1051f85afc3fcf7593ec5479fc8db01c58db", size = 1848472, upload-time = "2024-10-04T14:37:12.698Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/e3/34/21da583b9596e72bb8e95b6197dee0a44b96b9ea2c147fccabd43ca5515b/jax-0.4.35.tar.gz", hash = "sha256:c0c986993026b10bf6f607fecb7417377460254640766ce40f1fef3fd139c12e", size = 1861189, upload-time = "2024-10-22T20:56:36.379Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/06/f3/c499d358dd7f267a63d7d38ef54aadad82e28d2c28bafff15360c3091946/jax-0.4.34-py3-none-any.whl", hash = "sha256:b957ca1fc91f7343f91a186af9f19c7f342c946f95a8c11c7f1e5cdfe2e58d9e", size = 2144294, upload-time = "2024-10-04T14:37:10.265Z" },
+ { url = "https://files.pythonhosted.org/packages/62/20/6c57c50c0ccc645fea1895950f1e5cd02f961ee44b3ffe83617fa46b0c1d/jax-0.4.35-py3-none-any.whl", hash = "sha256:fa99e909a31424abfec750019a6dd36f6acc18a6e7d40e2c0086b932cc351325", size = 2158621, upload-time = "2024-10-22T20:56:34.071Z" },
]
[[package]]
name = "jaxlib"
version = "0.4.34"
-source = { registry = "https://pypi.org/simple" }
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
dependencies = [
{ name = "ml-dtypes" },
{ name = "numpy" },
@@ -425,7 +477,7 @@ wheels = [
[[package]]
name = "jaxopt"
version = "0.8.3"
-source = { registry = "https://pypi.org/simple" }
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
dependencies = [
{ name = "jax" },
{ name = "jaxlib" },
@@ -440,7 +492,7 @@ wheels = [
[[package]]
name = "jinja2"
version = "3.1.6"
-source = { registry = "https://pypi.org/simple" }
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
dependencies = [
{ name = "markupsafe" },
]
@@ -451,133 +503,154 @@ wheels = [
[[package]]
name = "kiwisolver"
-version = "1.4.9"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/5c/3c/85844f1b0feb11ee581ac23fe5fce65cd049a200c1446708cc1b7f922875/kiwisolver-1.4.9.tar.gz", hash = "sha256:c3b22c26c6fd6811b0ae8363b95ca8ce4ea3c202d3d0975b2914310ceb1bcc4d", size = 97564, upload-time = "2025-08-10T21:27:49.279Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/6f/ab/c80b0d5a9d8a1a65f4f815f2afff9798b12c3b9f31f1d304dd233dd920e2/kiwisolver-1.4.9-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:eb14a5da6dc7642b0f3a18f13654847cd8b7a2550e2645a5bda677862b03ba16", size = 124167, upload-time = "2025-08-10T21:25:53.403Z" },
- { url = "https://files.pythonhosted.org/packages/a0/c0/27fe1a68a39cf62472a300e2879ffc13c0538546c359b86f149cc19f6ac3/kiwisolver-1.4.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:39a219e1c81ae3b103643d2aedb90f1ef22650deb266ff12a19e7773f3e5f089", size = 66579, upload-time = "2025-08-10T21:25:54.79Z" },
- { url = "https://files.pythonhosted.org/packages/31/a2/a12a503ac1fd4943c50f9822678e8015a790a13b5490354c68afb8489814/kiwisolver-1.4.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2405a7d98604b87f3fc28b1716783534b1b4b8510d8142adca34ee0bc3c87543", size = 65309, upload-time = "2025-08-10T21:25:55.76Z" },
- { url = "https://files.pythonhosted.org/packages/66/e1/e533435c0be77c3f64040d68d7a657771194a63c279f55573188161e81ca/kiwisolver-1.4.9-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:dc1ae486f9abcef254b5618dfb4113dd49f94c68e3e027d03cf0143f3f772b61", size = 1435596, upload-time = "2025-08-10T21:25:56.861Z" },
- { url = "https://files.pythonhosted.org/packages/67/1e/51b73c7347f9aabdc7215aa79e8b15299097dc2f8e67dee2b095faca9cb0/kiwisolver-1.4.9-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a1f570ce4d62d718dce3f179ee78dac3b545ac16c0c04bb363b7607a949c0d1", size = 1246548, upload-time = "2025-08-10T21:25:58.246Z" },
- { url = "https://files.pythonhosted.org/packages/21/aa/72a1c5d1e430294f2d32adb9542719cfb441b5da368d09d268c7757af46c/kiwisolver-1.4.9-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb27e7b78d716c591e88e0a09a2139c6577865d7f2e152488c2cc6257f460872", size = 1263618, upload-time = "2025-08-10T21:25:59.857Z" },
- { url = "https://files.pythonhosted.org/packages/a3/af/db1509a9e79dbf4c260ce0cfa3903ea8945f6240e9e59d1e4deb731b1a40/kiwisolver-1.4.9-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:15163165efc2f627eb9687ea5f3a28137217d217ac4024893d753f46bce9de26", size = 1317437, upload-time = "2025-08-10T21:26:01.105Z" },
- { url = "https://files.pythonhosted.org/packages/e0/f2/3ea5ee5d52abacdd12013a94130436e19969fa183faa1e7c7fbc89e9a42f/kiwisolver-1.4.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bdee92c56a71d2b24c33a7d4c2856bd6419d017e08caa7802d2963870e315028", size = 2195742, upload-time = "2025-08-10T21:26:02.675Z" },
- { url = "https://files.pythonhosted.org/packages/6f/9b/1efdd3013c2d9a2566aa6a337e9923a00590c516add9a1e89a768a3eb2fc/kiwisolver-1.4.9-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:412f287c55a6f54b0650bd9b6dce5aceddb95864a1a90c87af16979d37c89771", size = 2290810, upload-time = "2025-08-10T21:26:04.009Z" },
- { url = "https://files.pythonhosted.org/packages/fb/e5/cfdc36109ae4e67361f9bc5b41323648cb24a01b9ade18784657e022e65f/kiwisolver-1.4.9-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2c93f00dcba2eea70af2be5f11a830a742fe6b579a1d4e00f47760ef13be247a", size = 2461579, upload-time = "2025-08-10T21:26:05.317Z" },
- { url = "https://files.pythonhosted.org/packages/62/86/b589e5e86c7610842213994cdea5add00960076bef4ae290c5fa68589cac/kiwisolver-1.4.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f117e1a089d9411663a3207ba874f31be9ac8eaa5b533787024dc07aeb74f464", size = 2268071, upload-time = "2025-08-10T21:26:06.686Z" },
- { url = "https://files.pythonhosted.org/packages/3b/c6/f8df8509fd1eee6c622febe54384a96cfaf4d43bf2ccec7a0cc17e4715c9/kiwisolver-1.4.9-cp311-cp311-win_amd64.whl", hash = "sha256:be6a04e6c79819c9a8c2373317d19a96048e5a3f90bec587787e86a1153883c2", size = 73840, upload-time = "2025-08-10T21:26:07.94Z" },
- { url = "https://files.pythonhosted.org/packages/e2/2d/16e0581daafd147bc11ac53f032a2b45eabac897f42a338d0a13c1e5c436/kiwisolver-1.4.9-cp311-cp311-win_arm64.whl", hash = "sha256:0ae37737256ba2de764ddc12aed4956460277f00c4996d51a197e72f62f5eec7", size = 65159, upload-time = "2025-08-10T21:26:09.048Z" },
- { url = "https://files.pythonhosted.org/packages/86/c9/13573a747838aeb1c76e3267620daa054f4152444d1f3d1a2324b78255b5/kiwisolver-1.4.9-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ac5a486ac389dddcc5bef4f365b6ae3ffff2c433324fb38dd35e3fab7c957999", size = 123686, upload-time = "2025-08-10T21:26:10.034Z" },
- { url = "https://files.pythonhosted.org/packages/51/ea/2ecf727927f103ffd1739271ca19c424d0e65ea473fbaeea1c014aea93f6/kiwisolver-1.4.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f2ba92255faa7309d06fe44c3a4a97efe1c8d640c2a79a5ef728b685762a6fd2", size = 66460, upload-time = "2025-08-10T21:26:11.083Z" },
- { url = "https://files.pythonhosted.org/packages/5b/5a/51f5464373ce2aeb5194508298a508b6f21d3867f499556263c64c621914/kiwisolver-1.4.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a2899935e724dd1074cb568ce7ac0dce28b2cd6ab539c8e001a8578eb106d14", size = 64952, upload-time = "2025-08-10T21:26:12.058Z" },
- { url = "https://files.pythonhosted.org/packages/70/90/6d240beb0f24b74371762873e9b7f499f1e02166a2d9c5801f4dbf8fa12e/kiwisolver-1.4.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f6008a4919fdbc0b0097089f67a1eb55d950ed7e90ce2cc3e640abadd2757a04", size = 1474756, upload-time = "2025-08-10T21:26:13.096Z" },
- { url = "https://files.pythonhosted.org/packages/12/42/f36816eaf465220f683fb711efdd1bbf7a7005a2473d0e4ed421389bd26c/kiwisolver-1.4.9-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:67bb8b474b4181770f926f7b7d2f8c0248cbcb78b660fdd41a47054b28d2a752", size = 1276404, upload-time = "2025-08-10T21:26:14.457Z" },
- { url = "https://files.pythonhosted.org/packages/2e/64/bc2de94800adc830c476dce44e9b40fd0809cddeef1fde9fcf0f73da301f/kiwisolver-1.4.9-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2327a4a30d3ee07d2fbe2e7933e8a37c591663b96ce42a00bc67461a87d7df77", size = 1294410, upload-time = "2025-08-10T21:26:15.73Z" },
- { url = "https://files.pythonhosted.org/packages/5f/42/2dc82330a70aa8e55b6d395b11018045e58d0bb00834502bf11509f79091/kiwisolver-1.4.9-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7a08b491ec91b1d5053ac177afe5290adacf1f0f6307d771ccac5de30592d198", size = 1343631, upload-time = "2025-08-10T21:26:17.045Z" },
- { url = "https://files.pythonhosted.org/packages/22/fd/f4c67a6ed1aab149ec5a8a401c323cee7a1cbe364381bb6c9c0d564e0e20/kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d8fc5c867c22b828001b6a38d2eaeb88160bf5783c6cb4a5e440efc981ce286d", size = 2224963, upload-time = "2025-08-10T21:26:18.737Z" },
- { url = "https://files.pythonhosted.org/packages/45/aa/76720bd4cb3713314677d9ec94dcc21ced3f1baf4830adde5bb9b2430a5f/kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:3b3115b2581ea35bb6d1f24a4c90af37e5d9b49dcff267eeed14c3893c5b86ab", size = 2321295, upload-time = "2025-08-10T21:26:20.11Z" },
- { url = "https://files.pythonhosted.org/packages/80/19/d3ec0d9ab711242f56ae0dc2fc5d70e298bb4a1f9dfab44c027668c673a1/kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:858e4c22fb075920b96a291928cb7dea5644e94c0ee4fcd5af7e865655e4ccf2", size = 2487987, upload-time = "2025-08-10T21:26:21.49Z" },
- { url = "https://files.pythonhosted.org/packages/39/e9/61e4813b2c97e86b6fdbd4dd824bf72d28bcd8d4849b8084a357bc0dd64d/kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ed0fecd28cc62c54b262e3736f8bb2512d8dcfdc2bcf08be5f47f96bf405b145", size = 2291817, upload-time = "2025-08-10T21:26:22.812Z" },
- { url = "https://files.pythonhosted.org/packages/a0/41/85d82b0291db7504da3c2defe35c9a8a5c9803a730f297bd823d11d5fb77/kiwisolver-1.4.9-cp312-cp312-win_amd64.whl", hash = "sha256:f68208a520c3d86ea51acf688a3e3002615a7f0238002cccc17affecc86a8a54", size = 73895, upload-time = "2025-08-10T21:26:24.37Z" },
- { url = "https://files.pythonhosted.org/packages/e2/92/5f3068cf15ee5cb624a0c7596e67e2a0bb2adee33f71c379054a491d07da/kiwisolver-1.4.9-cp312-cp312-win_arm64.whl", hash = "sha256:2c1a4f57df73965f3f14df20b80ee29e6a7930a57d2d9e8491a25f676e197c60", size = 64992, upload-time = "2025-08-10T21:26:25.732Z" },
- { url = "https://files.pythonhosted.org/packages/31/c1/c2686cda909742ab66c7388e9a1a8521a59eb89f8bcfbee28fc980d07e24/kiwisolver-1.4.9-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a5d0432ccf1c7ab14f9949eec60c5d1f924f17c037e9f8b33352fa05799359b8", size = 123681, upload-time = "2025-08-10T21:26:26.725Z" },
- { url = "https://files.pythonhosted.org/packages/ca/f0/f44f50c9f5b1a1860261092e3bc91ecdc9acda848a8b8c6abfda4a24dd5c/kiwisolver-1.4.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efb3a45b35622bb6c16dbfab491a8f5a391fe0e9d45ef32f4df85658232ca0e2", size = 66464, upload-time = "2025-08-10T21:26:27.733Z" },
- { url = "https://files.pythonhosted.org/packages/2d/7a/9d90a151f558e29c3936b8a47ac770235f436f2120aca41a6d5f3d62ae8d/kiwisolver-1.4.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1a12cf6398e8a0a001a059747a1cbf24705e18fe413bc22de7b3d15c67cffe3f", size = 64961, upload-time = "2025-08-10T21:26:28.729Z" },
- { url = "https://files.pythonhosted.org/packages/e9/e9/f218a2cb3a9ffbe324ca29a9e399fa2d2866d7f348ec3a88df87fc248fc5/kiwisolver-1.4.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b67e6efbf68e077dd71d1a6b37e43e1a99d0bff1a3d51867d45ee8908b931098", size = 1474607, upload-time = "2025-08-10T21:26:29.798Z" },
- { url = "https://files.pythonhosted.org/packages/d9/28/aac26d4c882f14de59041636292bc838db8961373825df23b8eeb807e198/kiwisolver-1.4.9-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5656aa670507437af0207645273ccdfee4f14bacd7f7c67a4306d0dcaeaf6eed", size = 1276546, upload-time = "2025-08-10T21:26:31.401Z" },
- { url = "https://files.pythonhosted.org/packages/8b/ad/8bfc1c93d4cc565e5069162f610ba2f48ff39b7de4b5b8d93f69f30c4bed/kiwisolver-1.4.9-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bfc08add558155345129c7803b3671cf195e6a56e7a12f3dde7c57d9b417f525", size = 1294482, upload-time = "2025-08-10T21:26:32.721Z" },
- { url = "https://files.pythonhosted.org/packages/da/f1/6aca55ff798901d8ce403206d00e033191f63d82dd708a186e0ed2067e9c/kiwisolver-1.4.9-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:40092754720b174e6ccf9e845d0d8c7d8e12c3d71e7fc35f55f3813e96376f78", size = 1343720, upload-time = "2025-08-10T21:26:34.032Z" },
- { url = "https://files.pythonhosted.org/packages/d1/91/eed031876c595c81d90d0f6fc681ece250e14bf6998c3d7c419466b523b7/kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:497d05f29a1300d14e02e6441cf0f5ee81c1ff5a304b0d9fb77423974684e08b", size = 2224907, upload-time = "2025-08-10T21:26:35.824Z" },
- { url = "https://files.pythonhosted.org/packages/e9/ec/4d1925f2e49617b9cca9c34bfa11adefad49d00db038e692a559454dfb2e/kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:bdd1a81a1860476eb41ac4bc1e07b3f07259e6d55bbf739b79c8aaedcf512799", size = 2321334, upload-time = "2025-08-10T21:26:37.534Z" },
- { url = "https://files.pythonhosted.org/packages/43/cb/450cd4499356f68802750c6ddc18647b8ea01ffa28f50d20598e0befe6e9/kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:e6b93f13371d341afee3be9f7c5964e3fe61d5fa30f6a30eb49856935dfe4fc3", size = 2488313, upload-time = "2025-08-10T21:26:39.191Z" },
- { url = "https://files.pythonhosted.org/packages/71/67/fc76242bd99f885651128a5d4fa6083e5524694b7c88b489b1b55fdc491d/kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d75aa530ccfaa593da12834b86a0724f58bff12706659baa9227c2ccaa06264c", size = 2291970, upload-time = "2025-08-10T21:26:40.828Z" },
- { url = "https://files.pythonhosted.org/packages/75/bd/f1a5d894000941739f2ae1b65a32892349423ad49c2e6d0771d0bad3fae4/kiwisolver-1.4.9-cp313-cp313-win_amd64.whl", hash = "sha256:dd0a578400839256df88c16abddf9ba14813ec5f21362e1fe65022e00c883d4d", size = 73894, upload-time = "2025-08-10T21:26:42.33Z" },
- { url = "https://files.pythonhosted.org/packages/95/38/dce480814d25b99a391abbddadc78f7c117c6da34be68ca8b02d5848b424/kiwisolver-1.4.9-cp313-cp313-win_arm64.whl", hash = "sha256:d4188e73af84ca82468f09cadc5ac4db578109e52acb4518d8154698d3a87ca2", size = 64995, upload-time = "2025-08-10T21:26:43.889Z" },
- { url = "https://files.pythonhosted.org/packages/e2/37/7d218ce5d92dadc5ebdd9070d903e0c7cf7edfe03f179433ac4d13ce659c/kiwisolver-1.4.9-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:5a0f2724dfd4e3b3ac5a82436a8e6fd16baa7d507117e4279b660fe8ca38a3a1", size = 126510, upload-time = "2025-08-10T21:26:44.915Z" },
- { url = "https://files.pythonhosted.org/packages/23/b0/e85a2b48233daef4b648fb657ebbb6f8367696a2d9548a00b4ee0eb67803/kiwisolver-1.4.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:1b11d6a633e4ed84fc0ddafd4ebfd8ea49b3f25082c04ad12b8315c11d504dc1", size = 67903, upload-time = "2025-08-10T21:26:45.934Z" },
- { url = "https://files.pythonhosted.org/packages/44/98/f2425bc0113ad7de24da6bb4dae1343476e95e1d738be7c04d31a5d037fd/kiwisolver-1.4.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61874cdb0a36016354853593cffc38e56fc9ca5aa97d2c05d3dcf6922cd55a11", size = 66402, upload-time = "2025-08-10T21:26:47.101Z" },
- { url = "https://files.pythonhosted.org/packages/98/d8/594657886df9f34c4177cc353cc28ca7e6e5eb562d37ccc233bff43bbe2a/kiwisolver-1.4.9-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:60c439763a969a6af93b4881db0eed8fadf93ee98e18cbc35bc8da868d0c4f0c", size = 1582135, upload-time = "2025-08-10T21:26:48.665Z" },
- { url = "https://files.pythonhosted.org/packages/5c/c6/38a115b7170f8b306fc929e166340c24958347308ea3012c2b44e7e295db/kiwisolver-1.4.9-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92a2f997387a1b79a75e7803aa7ded2cfbe2823852ccf1ba3bcf613b62ae3197", size = 1389409, upload-time = "2025-08-10T21:26:50.335Z" },
- { url = "https://files.pythonhosted.org/packages/bf/3b/e04883dace81f24a568bcee6eb3001da4ba05114afa622ec9b6fafdc1f5e/kiwisolver-1.4.9-cp313-cp313t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a31d512c812daea6d8b3be3b2bfcbeb091dbb09177706569bcfc6240dcf8b41c", size = 1401763, upload-time = "2025-08-10T21:26:51.867Z" },
- { url = "https://files.pythonhosted.org/packages/9f/80/20ace48e33408947af49d7d15c341eaee69e4e0304aab4b7660e234d6288/kiwisolver-1.4.9-cp313-cp313t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:52a15b0f35dad39862d376df10c5230155243a2c1a436e39eb55623ccbd68185", size = 1453643, upload-time = "2025-08-10T21:26:53.592Z" },
- { url = "https://files.pythonhosted.org/packages/64/31/6ce4380a4cd1f515bdda976a1e90e547ccd47b67a1546d63884463c92ca9/kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a30fd6fdef1430fd9e1ba7b3398b5ee4e2887783917a687d86ba69985fb08748", size = 2330818, upload-time = "2025-08-10T21:26:55.051Z" },
- { url = "https://files.pythonhosted.org/packages/fa/e9/3f3fcba3bcc7432c795b82646306e822f3fd74df0ee81f0fa067a1f95668/kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cc9617b46837c6468197b5945e196ee9ca43057bb7d9d1ae688101e4e1dddf64", size = 2419963, upload-time = "2025-08-10T21:26:56.421Z" },
- { url = "https://files.pythonhosted.org/packages/99/43/7320c50e4133575c66e9f7dadead35ab22d7c012a3b09bb35647792b2a6d/kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:0ab74e19f6a2b027ea4f845a78827969af45ce790e6cb3e1ebab71bdf9f215ff", size = 2594639, upload-time = "2025-08-10T21:26:57.882Z" },
- { url = "https://files.pythonhosted.org/packages/65/d6/17ae4a270d4a987ef8a385b906d2bdfc9fce502d6dc0d3aea865b47f548c/kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dba5ee5d3981160c28d5490f0d1b7ed730c22470ff7f6cc26cfcfaacb9896a07", size = 2391741, upload-time = "2025-08-10T21:26:59.237Z" },
- { url = "https://files.pythonhosted.org/packages/2a/8f/8f6f491d595a9e5912971f3f863d81baddccc8a4d0c3749d6a0dd9ffc9df/kiwisolver-1.4.9-cp313-cp313t-win_arm64.whl", hash = "sha256:0749fd8f4218ad2e851e11cc4dc05c7cbc0cbc4267bdfdb31782e65aace4ee9c", size = 68646, upload-time = "2025-08-10T21:27:00.52Z" },
- { url = "https://files.pythonhosted.org/packages/6b/32/6cc0fbc9c54d06c2969faa9c1d29f5751a2e51809dd55c69055e62d9b426/kiwisolver-1.4.9-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:9928fe1eb816d11ae170885a74d074f57af3a0d65777ca47e9aeb854a1fba386", size = 123806, upload-time = "2025-08-10T21:27:01.537Z" },
- { url = "https://files.pythonhosted.org/packages/b2/dd/2bfb1d4a4823d92e8cbb420fe024b8d2167f72079b3bb941207c42570bdf/kiwisolver-1.4.9-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d0005b053977e7b43388ddec89fa567f43d4f6d5c2c0affe57de5ebf290dc552", size = 66605, upload-time = "2025-08-10T21:27:03.335Z" },
- { url = "https://files.pythonhosted.org/packages/f7/69/00aafdb4e4509c2ca6064646cba9cd4b37933898f426756adb2cb92ebbed/kiwisolver-1.4.9-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2635d352d67458b66fd0667c14cb1d4145e9560d503219034a18a87e971ce4f3", size = 64925, upload-time = "2025-08-10T21:27:04.339Z" },
- { url = "https://files.pythonhosted.org/packages/43/dc/51acc6791aa14e5cb6d8a2e28cefb0dc2886d8862795449d021334c0df20/kiwisolver-1.4.9-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:767c23ad1c58c9e827b649a9ab7809fd5fd9db266a9cf02b0e926ddc2c680d58", size = 1472414, upload-time = "2025-08-10T21:27:05.437Z" },
- { url = "https://files.pythonhosted.org/packages/3d/bb/93fa64a81db304ac8a246f834d5094fae4b13baf53c839d6bb6e81177129/kiwisolver-1.4.9-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72d0eb9fba308b8311685c2268cf7d0a0639a6cd027d8128659f72bdd8a024b4", size = 1281272, upload-time = "2025-08-10T21:27:07.063Z" },
- { url = "https://files.pythonhosted.org/packages/70/e6/6df102916960fb8d05069d4bd92d6d9a8202d5a3e2444494e7cd50f65b7a/kiwisolver-1.4.9-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f68e4f3eeca8fb22cc3d731f9715a13b652795ef657a13df1ad0c7dc0e9731df", size = 1298578, upload-time = "2025-08-10T21:27:08.452Z" },
- { url = "https://files.pythonhosted.org/packages/7c/47/e142aaa612f5343736b087864dbaebc53ea8831453fb47e7521fa8658f30/kiwisolver-1.4.9-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d84cd4061ae292d8ac367b2c3fa3aad11cb8625a95d135fe93f286f914f3f5a6", size = 1345607, upload-time = "2025-08-10T21:27:10.125Z" },
- { url = "https://files.pythonhosted.org/packages/54/89/d641a746194a0f4d1a3670fb900d0dbaa786fb98341056814bc3f058fa52/kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a60ea74330b91bd22a29638940d115df9dc00af5035a9a2a6ad9399ffb4ceca5", size = 2230150, upload-time = "2025-08-10T21:27:11.484Z" },
- { url = "https://files.pythonhosted.org/packages/aa/6b/5ee1207198febdf16ac11f78c5ae40861b809cbe0e6d2a8d5b0b3044b199/kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ce6a3a4e106cf35c2d9c4fa17c05ce0b180db622736845d4315519397a77beaf", size = 2325979, upload-time = "2025-08-10T21:27:12.917Z" },
- { url = "https://files.pythonhosted.org/packages/fc/ff/b269eefd90f4ae14dcc74973d5a0f6d28d3b9bb1afd8c0340513afe6b39a/kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:77937e5e2a38a7b48eef0585114fe7930346993a88060d0bf886086d2aa49ef5", size = 2491456, upload-time = "2025-08-10T21:27:14.353Z" },
- { url = "https://files.pythonhosted.org/packages/fc/d4/10303190bd4d30de547534601e259a4fbf014eed94aae3e5521129215086/kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:24c175051354f4a28c5d6a31c93906dc653e2bf234e8a4bbfb964892078898ce", size = 2294621, upload-time = "2025-08-10T21:27:15.808Z" },
- { url = "https://files.pythonhosted.org/packages/28/e0/a9a90416fce5c0be25742729c2ea52105d62eda6c4be4d803c2a7be1fa50/kiwisolver-1.4.9-cp314-cp314-win_amd64.whl", hash = "sha256:0763515d4df10edf6d06a3c19734e2566368980d21ebec439f33f9eb936c07b7", size = 75417, upload-time = "2025-08-10T21:27:17.436Z" },
- { url = "https://files.pythonhosted.org/packages/1f/10/6949958215b7a9a264299a7db195564e87900f709db9245e4ebdd3c70779/kiwisolver-1.4.9-cp314-cp314-win_arm64.whl", hash = "sha256:0e4e2bf29574a6a7b7f6cb5fa69293b9f96c928949ac4a53ba3f525dffb87f9c", size = 66582, upload-time = "2025-08-10T21:27:18.436Z" },
- { url = "https://files.pythonhosted.org/packages/ec/79/60e53067903d3bc5469b369fe0dfc6b3482e2133e85dae9daa9527535991/kiwisolver-1.4.9-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d976bbb382b202f71c67f77b0ac11244021cfa3f7dfd9e562eefcea2df711548", size = 126514, upload-time = "2025-08-10T21:27:19.465Z" },
- { url = "https://files.pythonhosted.org/packages/25/d1/4843d3e8d46b072c12a38c97c57fab4608d36e13fe47d47ee96b4d61ba6f/kiwisolver-1.4.9-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2489e4e5d7ef9a1c300a5e0196e43d9c739f066ef23270607d45aba368b91f2d", size = 67905, upload-time = "2025-08-10T21:27:20.51Z" },
- { url = "https://files.pythonhosted.org/packages/8c/ae/29ffcbd239aea8b93108de1278271ae764dfc0d803a5693914975f200596/kiwisolver-1.4.9-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e2ea9f7ab7fbf18fffb1b5434ce7c69a07582f7acc7717720f1d69f3e806f90c", size = 66399, upload-time = "2025-08-10T21:27:21.496Z" },
- { url = "https://files.pythonhosted.org/packages/a1/ae/d7ba902aa604152c2ceba5d352d7b62106bedbccc8e95c3934d94472bfa3/kiwisolver-1.4.9-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b34e51affded8faee0dfdb705416153819d8ea9250bbbf7ea1b249bdeb5f1122", size = 1582197, upload-time = "2025-08-10T21:27:22.604Z" },
- { url = "https://files.pythonhosted.org/packages/f2/41/27c70d427eddb8bc7e4f16420a20fefc6f480312122a59a959fdfe0445ad/kiwisolver-1.4.9-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8aacd3d4b33b772542b2e01beb50187536967b514b00003bdda7589722d2a64", size = 1390125, upload-time = "2025-08-10T21:27:24.036Z" },
- { url = "https://files.pythonhosted.org/packages/41/42/b3799a12bafc76d962ad69083f8b43b12bf4fe78b097b12e105d75c9b8f1/kiwisolver-1.4.9-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7cf974dd4e35fa315563ac99d6287a1024e4dc2077b8a7d7cd3d2fb65d283134", size = 1402612, upload-time = "2025-08-10T21:27:25.773Z" },
- { url = "https://files.pythonhosted.org/packages/d2/b5/a210ea073ea1cfaca1bb5c55a62307d8252f531beb364e18aa1e0888b5a0/kiwisolver-1.4.9-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:85bd218b5ecfbee8c8a82e121802dcb519a86044c9c3b2e4aef02fa05c6da370", size = 1453990, upload-time = "2025-08-10T21:27:27.089Z" },
- { url = "https://files.pythonhosted.org/packages/5f/ce/a829eb8c033e977d7ea03ed32fb3c1781b4fa0433fbadfff29e39c676f32/kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0856e241c2d3df4efef7c04a1e46b1936b6120c9bcf36dd216e3acd84bc4fb21", size = 2331601, upload-time = "2025-08-10T21:27:29.343Z" },
- { url = "https://files.pythonhosted.org/packages/e0/4b/b5e97eb142eb9cd0072dacfcdcd31b1c66dc7352b0f7c7255d339c0edf00/kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:9af39d6551f97d31a4deebeac6f45b156f9755ddc59c07b402c148f5dbb6482a", size = 2422041, upload-time = "2025-08-10T21:27:30.754Z" },
- { url = "https://files.pythonhosted.org/packages/40/be/8eb4cd53e1b85ba4edc3a9321666f12b83113a178845593307a3e7891f44/kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:bb4ae2b57fc1d8cbd1cf7b1d9913803681ffa903e7488012be5b76dedf49297f", size = 2594897, upload-time = "2025-08-10T21:27:32.803Z" },
- { url = "https://files.pythonhosted.org/packages/99/dd/841e9a66c4715477ea0abc78da039832fbb09dac5c35c58dc4c41a407b8a/kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:aedff62918805fb62d43a4aa2ecd4482c380dc76cd31bd7c8878588a61bd0369", size = 2391835, upload-time = "2025-08-10T21:27:34.23Z" },
- { url = "https://files.pythonhosted.org/packages/0c/28/4b2e5c47a0da96896fdfdb006340ade064afa1e63675d01ea5ac222b6d52/kiwisolver-1.4.9-cp314-cp314t-win_amd64.whl", hash = "sha256:1fa333e8b2ce4d9660f2cda9c0e1b6bafcfb2457a9d259faa82289e73ec24891", size = 79988, upload-time = "2025-08-10T21:27:35.587Z" },
- { url = "https://files.pythonhosted.org/packages/80/be/3578e8afd18c88cdf9cb4cffde75a96d2be38c5a903f1ed0ceec061bd09e/kiwisolver-1.4.9-cp314-cp314t-win_arm64.whl", hash = "sha256:4a48a2ce79d65d363597ef7b567ce3d14d68783d2b2263d98db3d9477805ba32", size = 70260, upload-time = "2025-08-10T21:27:36.606Z" },
- { url = "https://files.pythonhosted.org/packages/a3/0f/36d89194b5a32c054ce93e586d4049b6c2c22887b0eb229c61c68afd3078/kiwisolver-1.4.9-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:720e05574713db64c356e86732c0f3c5252818d05f9df320f0ad8380641acea5", size = 60104, upload-time = "2025-08-10T21:27:43.287Z" },
- { url = "https://files.pythonhosted.org/packages/52/ba/4ed75f59e4658fd21fe7dde1fee0ac397c678ec3befba3fe6482d987af87/kiwisolver-1.4.9-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:17680d737d5335b552994a2008fab4c851bcd7de33094a82067ef3a576ff02fa", size = 58592, upload-time = "2025-08-10T21:27:44.314Z" },
- { url = "https://files.pythonhosted.org/packages/33/01/a8ea7c5ea32a9b45ceeaee051a04c8ed4320f5add3c51bfa20879b765b70/kiwisolver-1.4.9-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:85b5352f94e490c028926ea567fc569c52ec79ce131dadb968d3853e809518c2", size = 80281, upload-time = "2025-08-10T21:27:45.369Z" },
- { url = "https://files.pythonhosted.org/packages/da/e3/dbd2ecdce306f1d07a1aaf324817ee993aab7aee9db47ceac757deabafbe/kiwisolver-1.4.9-pp311-pypy311_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:464415881e4801295659462c49461a24fb107c140de781d55518c4b80cb6790f", size = 78009, upload-time = "2025-08-10T21:27:46.376Z" },
- { url = "https://files.pythonhosted.org/packages/da/e9/0d4add7873a73e462aeb45c036a2dead2562b825aa46ba326727b3f31016/kiwisolver-1.4.9-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:fb940820c63a9590d31d88b815e7a3aa5915cad3ce735ab45f0c730b39547de1", size = 73929, upload-time = "2025-08-10T21:27:48.236Z" },
+version = "1.5.0"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d0/67/9c61eccb13f0bdca9307614e782fec49ffdde0f7a2314935d489fa93cd9c/kiwisolver-1.5.0.tar.gz", hash = "sha256:d4193f3d9dc3f6f79aaed0e5637f45d98850ebf01f7ca20e69457f3e8946b66a", size = 103482, upload-time = "2026-03-09T13:15:53.382Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/12/dd/a495a9c104be1c476f0386e714252caf2b7eca883915422a64c50b88c6f5/kiwisolver-1.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9eed0f7edbb274413b6ee781cca50541c8c0facd3d6fd289779e494340a2b85c", size = 122798, upload-time = "2026-03-09T13:12:58.963Z" },
+ { url = "https://files.pythonhosted.org/packages/11/60/37b4047a2af0cf5ef6d8b4b26e91829ae6fc6a2d1f74524bcb0e7cd28a32/kiwisolver-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c4923e404d6bcd91b6779c009542e5647fef32e4a5d75e115e3bbac6f2335eb", size = 66216, upload-time = "2026-03-09T13:13:00.155Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/aa/510dc933d87767584abfe03efa445889996c70c2990f6f87c3ebaa0a18c5/kiwisolver-1.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0df54df7e686afa55e6f21fb86195224a6d9beb71d637e8d7920c95cf0f89aac", size = 63911, upload-time = "2026-03-09T13:13:01.671Z" },
+ { url = "https://files.pythonhosted.org/packages/80/46/bddc13df6c2a40741e0cc7865bb1c9ed4796b6760bd04ce5fae3928ef917/kiwisolver-1.5.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2517e24d7315eb51c10664cdb865195df38ab74456c677df67bb47f12d088a27", size = 1438209, upload-time = "2026-03-09T13:13:03.385Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/d6/76621246f5165e5372f02f5e6f3f48ea336a8f9e96e43997d45b240ed8cd/kiwisolver-1.5.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff710414307fefa903e0d9bdf300972f892c23477829f49504e59834f4195398", size = 1248888, upload-time = "2026-03-09T13:13:05.231Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/c1/31559ec6fb39a5b48035ce29bb63ade628f321785f38c384dee3e2c08bc1/kiwisolver-1.5.0-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6176c1811d9d5a04fa391c490cc44f451e240697a16977f11c6f722efb9041db", size = 1266304, upload-time = "2026-03-09T13:13:06.743Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/ef/1cb8276f2d29cc6a41e0a042f27946ca347d3a4a75acf85d0a16aa6dcc82/kiwisolver-1.5.0-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50847dca5d197fcbd389c805aa1a1cf32f25d2e7273dc47ab181a517666b68cc", size = 1319650, upload-time = "2026-03-09T13:13:08.607Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/e4/5ba3cecd7ce6236ae4a80f67e5d5531287337d0e1f076ca87a5abe4cd5d0/kiwisolver-1.5.0-cp311-cp311-manylinux_2_39_riscv64.whl", hash = "sha256:01808c6d15f4c3e8559595d6d1fe6411c68e4a3822b4b9972b44473b24f4e679", size = 970949, upload-time = "2026-03-09T13:13:10.299Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/69/dc61f7ae9a2f071f26004ced87f078235b5507ab6e5acd78f40365655034/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f1f9f4121ec58628c96baa3de1a55a4e3a333c5102c8e94b64e23bf7b2083309", size = 2199125, upload-time = "2026-03-09T13:13:11.841Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/7b/abbe0f1b5afa85f8d084b73e90e5f801c0939eba16ac2e49af7c61a6c28d/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b7d335370ae48a780c6e6a6bbfa97342f563744c39c35562f3f367665f5c1de2", size = 2293783, upload-time = "2026-03-09T13:13:14.399Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/80/5908ae149d96d81580d604c7f8aefd0e98f4fd728cf172f477e9f2a81744/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:800ee55980c18545af444d93fdd60c56b580db5cc54867d8cbf8a1dc0829938c", size = 1960726, upload-time = "2026-03-09T13:13:16.047Z" },
+ { url = "https://files.pythonhosted.org/packages/84/08/a78cb776f8c085b7143142ce479859cfec086bd09ee638a317040b6ef420/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:c438f6ca858697c9ab67eb28246c92508af972e114cac34e57a6d4ba17a3ac08", size = 2464738, upload-time = "2026-03-09T13:13:17.897Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/e1/65584da5356ed6cb12c63791a10b208860ac40a83de165cb6a6751a686e3/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8c63c91f95173f9c2a67c7c526b2cea976828a0e7fced9cdcead2802dc10f8a4", size = 2270718, upload-time = "2026-03-09T13:13:19.421Z" },
+ { url = "https://files.pythonhosted.org/packages/be/6c/28f17390b62b8f2f520e2915095b3c94d88681ecf0041e75389d9667f202/kiwisolver-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:beb7f344487cdcb9e1efe4b7a29681b74d34c08f0043a327a74da852a6749e7b", size = 73480, upload-time = "2026-03-09T13:13:20.818Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/0e/2ee5debc4f77a625778fec5501ff3e8036fe361b7ee28ae402a485bb9694/kiwisolver-1.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:ad4ae4ffd1ee9cd11357b4c66b612da9888f4f4daf2f36995eda64bd45370cac", size = 64930, upload-time = "2026-03-09T13:13:21.997Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/b2/818b74ebea34dabe6d0c51cb1c572e046730e64844da6ed646d5298c40ce/kiwisolver-1.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:4e9750bc21b886308024f8a54ccb9a2cc38ac9fa813bf4348434e3d54f337ff9", size = 123158, upload-time = "2026-03-09T13:13:23.127Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/d9/405320f8077e8e1c5c4bd6adc45e1e6edf6d727b6da7f2e2533cf58bff71/kiwisolver-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:72ec46b7eba5b395e0a7b63025490d3214c11013f4aacb4f5e8d6c3041829588", size = 66388, upload-time = "2026-03-09T13:13:24.765Z" },
+ { url = "https://files.pythonhosted.org/packages/99/9f/795fedf35634f746151ca8839d05681ceb6287fbed6cc1c9bf235f7887c2/kiwisolver-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ed3a984b31da7481b103f68776f7128a89ef26ed40f4dc41a2223cda7fb24819", size = 64068, upload-time = "2026-03-09T13:13:25.878Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/13/680c54afe3e65767bed7ec1a15571e1a2f1257128733851ade24abcefbcc/kiwisolver-1.5.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bb5136fb5352d3f422df33f0c879a1b0c204004324150cc3b5e3c4f310c9049f", size = 1477934, upload-time = "2026-03-09T13:13:27.166Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/2f/cebfcdb60fd6a9b0f6b47a9337198bcbad6fbe15e68189b7011fd914911f/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b2af221f268f5af85e776a73d62b0845fc8baf8ef0abfae79d29c77d0e776aaf", size = 1278537, upload-time = "2026-03-09T13:13:28.707Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/0d/9b782923aada3fafb1d6b84e13121954515c669b18af0c26e7d21f579855/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b0f172dc8ffaccb8522d7c5d899de00133f2f1ca7b0a49b7da98e901de87bf2d", size = 1296685, upload-time = "2026-03-09T13:13:30.528Z" },
+ { url = "https://files.pythonhosted.org/packages/27/70/83241b6634b04fe44e892688d5208332bde130f38e610c0418f9ede47ded/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6ab8ba9152203feec73758dad83af9a0bbe05001eb4639e547207c40cfb52083", size = 1346024, upload-time = "2026-03-09T13:13:32.818Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/db/30ed226fb271ae1a6431fc0fe0edffb2efe23cadb01e798caeb9f2ceae8f/kiwisolver-1.5.0-cp312-cp312-manylinux_2_39_riscv64.whl", hash = "sha256:cdee07c4d7f6d72008d3f73b9bf027f4e11550224c7c50d8df1ae4a37c1402a6", size = 987241, upload-time = "2026-03-09T13:13:34.435Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/bd/c314595208e4c9587652d50959ead9e461995389664e490f4dce7ff0f782/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7c60d3c9b06fb23bd9c6139281ccbdc384297579ae037f08ae90c69f6845c0b1", size = 2227742, upload-time = "2026-03-09T13:13:36.4Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/43/0499cec932d935229b5543d073c2b87c9c22846aab48881e9d8d6e742a2d/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e315e5ec90d88e140f57696ff85b484ff68bb311e36f2c414aa4286293e6dee0", size = 2323966, upload-time = "2026-03-09T13:13:38.204Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/6f/79b0d760907965acfd9d61826a3d41f8f093c538f55cd2633d3f0db269f6/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:1465387ac63576c3e125e5337a6892b9e99e0627d52317f3ca79e6930d889d15", size = 1977417, upload-time = "2026-03-09T13:13:39.966Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/31/01d0537c41cb75a551a438c3c7a80d0c60d60b81f694dac83dd436aec0d0/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:530a3fd64c87cffa844d4b6b9768774763d9caa299e9b75d8eca6a4423b31314", size = 2491238, upload-time = "2026-03-09T13:13:41.698Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/34/8aefdd0be9cfd00a44509251ba864f5caf2991e36772e61c408007e7f417/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1d9daea4ea6b9be74fe2f01f7fbade8d6ffab263e781274cffca0dba9be9eec9", size = 2294947, upload-time = "2026-03-09T13:13:43.343Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/cf/0348374369ca588f8fe9c338fae49fa4e16eeb10ffb3d012f23a54578a9e/kiwisolver-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:f18c2d9782259a6dc132fdc7a63c168cbc74b35284b6d75c673958982a378384", size = 73569, upload-time = "2026-03-09T13:13:45.792Z" },
+ { url = "https://files.pythonhosted.org/packages/28/26/192b26196e2316e2bd29deef67e37cdf9870d9af8e085e521afff0fed526/kiwisolver-1.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:f7c7553b13f69c1b29a5bde08ddc6d9d0c8bfb84f9ed01c30db25944aeb852a7", size = 64997, upload-time = "2026-03-09T13:13:46.878Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/69/024d6711d5ba575aa65d5538042e99964104e97fa153a9f10bc369182bc2/kiwisolver-1.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:fd40bb9cd0891c4c3cb1ddf83f8bbfa15731a248fdc8162669405451e2724b09", size = 123166, upload-time = "2026-03-09T13:13:48.032Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/48/adbb40df306f587054a348831220812b9b1d787aff714cfbc8556e38fccd/kiwisolver-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c0e1403fd7c26d77c1f03e096dc58a5c726503fa0db0456678b8668f76f521e3", size = 66395, upload-time = "2026-03-09T13:13:49.365Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/3a/d0a972b34e1c63e2409413104216cd1caa02c5a37cb668d1687d466c1c45/kiwisolver-1.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dda366d548e89a90d88a86c692377d18d8bd64b39c1fb2b92cb31370e2896bbd", size = 64065, upload-time = "2026-03-09T13:13:50.562Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/0a/7b98e1e119878a27ba8618ca1e18b14f992ff1eda40f47bccccf4de44121/kiwisolver-1.5.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:332b4f0145c30b5f5ad9374881133e5aa64320428a57c2c2b61e9d891a51c2f3", size = 1477903, upload-time = "2026-03-09T13:13:52.084Z" },
+ { url = "https://files.pythonhosted.org/packages/18/d8/55638d89ffd27799d5cc3d8aa28e12f4ce7a64d67b285114dbedc8ea4136/kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c50b89ffd3e1a911c69a1dd3de7173c0cd10b130f56222e57898683841e4f96", size = 1278751, upload-time = "2026-03-09T13:13:54.673Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/97/b4c8d0d18421ecceba20ad8701358453b88e32414e6f6950b5a4bad54e65/kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4db576bb8c3ef9365f8b40fe0f671644de6736ae2c27a2c62d7d8a1b4329f099", size = 1296793, upload-time = "2026-03-09T13:13:56.287Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/10/f862f94b6389d8957448ec9df59450b81bec4abb318805375c401a1e6892/kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0b85aad90cea8ac6797a53b5d5f2e967334fa4d1149f031c4537569972596cb8", size = 1346041, upload-time = "2026-03-09T13:13:58.269Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/6a/f1650af35821eaf09de398ec0bc2aefc8f211f0cda50204c9f1673741ba9/kiwisolver-1.5.0-cp313-cp313-manylinux_2_39_riscv64.whl", hash = "sha256:d36ca54cb4c6c4686f7cbb7b817f66f5911c12ddb519450bbe86707155028f87", size = 987292, upload-time = "2026-03-09T13:13:59.871Z" },
+ { url = "https://files.pythonhosted.org/packages/de/19/d7fb82984b9238115fe629c915007be608ebd23dc8629703d917dbfaffd4/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:38f4a703656f493b0ad185211ccfca7f0386120f022066b018eb5296d8613e23", size = 2227865, upload-time = "2026-03-09T13:14:01.401Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/b9/46b7f386589fd222dac9e9de9c956ce5bcefe2ee73b4e79891381dda8654/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3ac2360e93cb41be81121755c6462cff3beaa9967188c866e5fce5cf13170859", size = 2324369, upload-time = "2026-03-09T13:14:02.972Z" },
+ { url = "https://files.pythonhosted.org/packages/92/8b/95e237cf3d9c642960153c769ddcbe278f182c8affb20cecc1cc983e7cc5/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c95cab08d1965db3d84a121f1c7ce7479bdd4072c9b3dafd8fecce48a2e6b902", size = 1977989, upload-time = "2026-03-09T13:14:04.503Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/95/980c9df53501892784997820136c01f62bc1865e31b82b9560f980c0e649/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fc20894c3d21194d8041a28b65622d5b86db786da6e3cfe73f0c762951a61167", size = 2491645, upload-time = "2026-03-09T13:14:06.106Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/32/900647fd0840abebe1561792c6b31e6a7c0e278fc3973d30572a965ca14c/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7a32f72973f0f950c1920475d5c5ea3d971b81b6f0ec53b8d0a956cc965f22e0", size = 2295237, upload-time = "2026-03-09T13:14:08.891Z" },
+ { url = "https://files.pythonhosted.org/packages/be/8a/be60e3bbcf513cc5a50f4a3e88e1dcecebb79c1ad607a7222877becaa101/kiwisolver-1.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:0bf3acf1419fa93064a4c2189ac0b58e3be7872bf6ee6177b0d4c63dc4cea276", size = 73573, upload-time = "2026-03-09T13:14:12.327Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/d2/64be2e429eb4fca7f7e1c52a91b12663aeaf25de3895e5cca0f47ef2a8d0/kiwisolver-1.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:fa8eb9ecdb7efb0b226acec134e0d709e87a909fa4971a54c0c4f6e88635484c", size = 64998, upload-time = "2026-03-09T13:14:13.469Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/69/ce68dd0c85755ae2de490bf015b62f2cea5f6b14ff00a463f9d0774449ff/kiwisolver-1.5.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:db485b3847d182b908b483b2ed133c66d88d49cacf98fd278fadafe11b4478d1", size = 125700, upload-time = "2026-03-09T13:14:14.636Z" },
+ { url = "https://files.pythonhosted.org/packages/74/aa/937aac021cf9d4349990d47eb319309a51355ed1dbdc9c077cdc9224cb11/kiwisolver-1.5.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:be12f931839a3bdfe28b584db0e640a65a8bcbc24560ae3fdb025a449b3d754e", size = 67537, upload-time = "2026-03-09T13:14:15.808Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/20/3a87fbece2c40ad0f6f0aefa93542559159c5f99831d596050e8afae7a9f/kiwisolver-1.5.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:16b85d37c2cbb3253226d26e64663f755d88a03439a9c47df6246b35defbdfb7", size = 65514, upload-time = "2026-03-09T13:14:18.035Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/7f/f943879cda9007c45e1f7dba216d705c3a18d6b35830e488b6c6a4e7cdf0/kiwisolver-1.5.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4432b835675f0ea7414aab3d37d119f7226d24869b7a829caeab49ebda407b0c", size = 1584848, upload-time = "2026-03-09T13:14:19.745Z" },
+ { url = "https://files.pythonhosted.org/packages/37/f8/4d4f85cc1870c127c88d950913370dd76138482161cd07eabbc450deff01/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b0feb50971481a2cc44d94e88bdb02cdd497618252ae226b8eb1201b957e368", size = 1391542, upload-time = "2026-03-09T13:14:21.54Z" },
+ { url = "https://files.pythonhosted.org/packages/04/0b/65dd2916c84d252b244bd405303220f729e7c17c9d7d33dca6feeff9ffc4/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:56fa888f10d0f367155e76ce849fa1166fc9730d13bd2d65a2aa13b6f5424489", size = 1404447, upload-time = "2026-03-09T13:14:23.205Z" },
+ { url = "https://files.pythonhosted.org/packages/39/5c/2606a373247babce9b1d056c03a04b65f3cf5290a8eac5d7bdead0a17e21/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:940dda65d5e764406b9fb92761cbf462e4e63f712ab60ed98f70552e496f3bf1", size = 1455918, upload-time = "2026-03-09T13:14:24.74Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/d1/c6078b5756670658e9192a2ef11e939c92918833d2745f85cd14a6004bdf/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_39_riscv64.whl", hash = "sha256:89fc958c702ee9a745e4700378f5d23fddbc46ff89e8fdbf5395c24d5c1452a3", size = 1072856, upload-time = "2026-03-09T13:14:26.597Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/c8/7def6ddf16eb2b3741d8b172bdaa9af882b03c78e9b0772975408801fa63/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9027d773c4ff81487181a925945743413f6069634d0b122d0b37684ccf4f1e18", size = 2333580, upload-time = "2026-03-09T13:14:28.237Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/87/2ac1fce0eb1e616fcd3c35caa23e665e9b1948bb984f4764790924594128/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:5b233ea3e165e43e35dba1d2b8ecc21cf070b45b65ae17dd2747d2713d942021", size = 2423018, upload-time = "2026-03-09T13:14:30.018Z" },
+ { url = "https://files.pythonhosted.org/packages/67/13/c6700ccc6cc218716bfcda4935e4b2997039869b4ad8a94f364c5a3b8e63/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:ce9bf03dad3b46408c08649c6fbd6ca28a9fce0eb32fdfffa6775a13103b5310", size = 2062804, upload-time = "2026-03-09T13:14:32.888Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/bd/877056304626943ff0f1f44c08f584300c199b887cb3176cd7e34f1515f1/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:fc4d3f1fb9ca0ae9f97b095963bc6326f1dbfd3779d6679a1e016b9baaa153d3", size = 2597482, upload-time = "2026-03-09T13:14:34.971Z" },
+ { url = "https://files.pythonhosted.org/packages/75/19/c60626c47bf0f8ac5dcf72c6c98e266d714f2fbbfd50cf6dab5ede3aaa50/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f443b4825c50a51ee68585522ab4a1d1257fac65896f282b4c6763337ac9f5d2", size = 2394328, upload-time = "2026-03-09T13:14:36.816Z" },
+ { url = "https://files.pythonhosted.org/packages/47/84/6a6d5e5bb8273756c27b7d810d47f7ef2f1f9b9fd23c9ee9a3f8c75c9cef/kiwisolver-1.5.0-cp313-cp313t-win_arm64.whl", hash = "sha256:893ff3a711d1b515ba9da14ee090519bad4610ed1962fbe298a434e8c5f8db53", size = 68410, upload-time = "2026-03-09T13:14:38.695Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/d7/060f45052f2a01ad5762c8fdecd6d7a752b43400dc29ff75cd47225a40fd/kiwisolver-1.5.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8df31fe574b8b3993cc61764f40941111b25c2d9fea13d3ce24a49907cd2d615", size = 123231, upload-time = "2026-03-09T13:14:41.323Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/a7/78da680eadd06ff35edef6ef68a1ad273bad3e2a0936c9a885103230aece/kiwisolver-1.5.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:1d49a49ac4cbfb7c1375301cd1ec90169dfeae55ff84710d782260ce77a75a02", size = 66489, upload-time = "2026-03-09T13:14:42.534Z" },
+ { url = "https://files.pythonhosted.org/packages/49/b2/97980f3ad4fae37dd7fe31626e2bf75fbf8bdf5d303950ec1fab39a12da8/kiwisolver-1.5.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0cbe94b69b819209a62cb27bdfa5dc2a8977d8de2f89dfd97ba4f53ed3af754e", size = 64063, upload-time = "2026-03-09T13:14:44.759Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/f9/b06c934a6aa8bc91f566bd2a214fd04c30506c2d9e2b6b171953216a65b6/kiwisolver-1.5.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:80aa065ffd378ff784822a6d7c3212f2d5f5e9c3589614b5c228b311fd3063ac", size = 1475913, upload-time = "2026-03-09T13:14:46.247Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/f0/f768ae564a710135630672981231320bc403cf9152b5596ec5289de0f106/kiwisolver-1.5.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e7f886f47ab881692f278ae901039a234e4025a68e6dfab514263a0b1c4ae05", size = 1282782, upload-time = "2026-03-09T13:14:48.458Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/9f/1de7aad00697325f05238a5f2eafbd487fb637cc27a558b5367a5f37fb7f/kiwisolver-1.5.0-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5060731cc3ed12ca3a8b57acd4aeca5bbc2f49216dd0bec1650a1acd89486bcd", size = 1300815, upload-time = "2026-03-09T13:14:50.721Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/c2/297f25141d2e468e0ce7f7a7b92e0cf8918143a0cbd3422c1ad627e85a06/kiwisolver-1.5.0-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7a4aa69609f40fce3cbc3f87b2061f042eee32f94b8f11db707b66a26461591a", size = 1347925, upload-time = "2026-03-09T13:14:52.304Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/d3/f4c73a02eb41520c47610207b21afa8cdd18fdbf64ffd94674ae21c4812d/kiwisolver-1.5.0-cp314-cp314-manylinux_2_39_riscv64.whl", hash = "sha256:d168fda2dbff7b9b5f38e693182d792a938c31db4dac3a80a4888de603c99554", size = 991322, upload-time = "2026-03-09T13:14:54.637Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/46/d3f2efef7732fcda98d22bf4ad5d3d71d545167a852ca710a494f4c15343/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:413b820229730d358efd838ecbab79902fe97094565fdc80ddb6b0a18c18a581", size = 2232857, upload-time = "2026-03-09T13:14:56.471Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/ec/2d9756bf2b6d26ae4349b8d3662fb3993f16d80c1f971c179ce862b9dbae/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:5124d1ea754509b09e53738ec185584cc609aae4a3b510aaf4ed6aa047ef9303", size = 2329376, upload-time = "2026-03-09T13:14:58.072Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/9f/876a0a0f2260f1bde92e002b3019a5fabc35e0939c7d945e0fa66185eb20/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e4415a8db000bf49a6dd1c478bf70062eaacff0f462b92b0ba68791a905861f9", size = 1982549, upload-time = "2026-03-09T13:14:59.668Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/4f/ba3624dfac23a64d54ac4179832860cb537c1b0af06024936e82ca4154a0/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d618fd27420381a4f6044faa71f46d8bfd911bd077c555f7138ed88729bfbe79", size = 2494680, upload-time = "2026-03-09T13:15:01.364Z" },
+ { url = "https://files.pythonhosted.org/packages/39/b7/97716b190ab98911b20d10bf92eca469121ec483b8ce0edd314f51bc85af/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5092eb5b1172947f57d6ea7d89b2f29650414e4293c47707eb499ec07a0ac796", size = 2297905, upload-time = "2026-03-09T13:15:03.925Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/36/4e551e8aa55c9188bca9abb5096805edbf7431072b76e2298e34fd3a3008/kiwisolver-1.5.0-cp314-cp314-win_amd64.whl", hash = "sha256:d76e2d8c75051d58177e762164d2e9ab92886534e3a12e795f103524f221dd8e", size = 75086, upload-time = "2026-03-09T13:15:07.775Z" },
+ { url = "https://files.pythonhosted.org/packages/70/15/9b90f7df0e31a003c71649cf66ef61c3c1b862f48c81007fa2383c8bd8d7/kiwisolver-1.5.0-cp314-cp314-win_arm64.whl", hash = "sha256:fa6248cd194edff41d7ea9425ced8ca3a6f838bfb295f6f1d6e6bb694a8518df", size = 66577, upload-time = "2026-03-09T13:15:09.139Z" },
+ { url = "https://files.pythonhosted.org/packages/17/01/7dc8c5443ff42b38e72731643ed7cf1ed9bf01691ae5cdca98501999ed83/kiwisolver-1.5.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:d1ffeb80b5676463d7a7d56acbe8e37a20ce725570e09549fe738e02ca6b7e1e", size = 125794, upload-time = "2026-03-09T13:15:10.525Z" },
+ { url = "https://files.pythonhosted.org/packages/46/8a/b4ebe46ebaac6a303417fab10c2e165c557ddaff558f9699d302b256bc53/kiwisolver-1.5.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:bc4d8e252f532ab46a1de9349e2d27b91fce46736a9eedaa37beaca66f574ed4", size = 67646, upload-time = "2026-03-09T13:15:12.016Z" },
+ { url = "https://files.pythonhosted.org/packages/60/35/10a844afc5f19d6f567359bf4789e26661755a2f36200d5d1ed8ad0126e5/kiwisolver-1.5.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6783e069732715ad0c3ce96dbf21dbc2235ab0593f2baf6338101f70371f4028", size = 65511, upload-time = "2026-03-09T13:15:13.311Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/8a/685b297052dd041dcebce8e8787b58923b6e78acc6115a0dc9189011c44b/kiwisolver-1.5.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e7c4c09a490dc4d4a7f8cbee56c606a320f9dc28cf92a7157a39d1ce7676a657", size = 1584858, upload-time = "2026-03-09T13:15:15.103Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/80/04865e3d4638ac5bddec28908916df4a3075b8c6cc101786a96803188b96/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2a075bd7bd19c70cf67c8badfa36cf7c5d8de3c9ddb8420c51e10d9c50e94920", size = 1392539, upload-time = "2026-03-09T13:15:16.661Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/01/77a19cacc0893fa13fafa46d1bba06fb4dc2360b3292baf4b56d8e067b24/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bdd3e53429ff02aa319ba59dfe4ceeec345bf46cf180ec2cf6fd5b942e7975e9", size = 1405310, upload-time = "2026-03-09T13:15:18.229Z" },
+ { url = "https://files.pythonhosted.org/packages/53/39/bcaf5d0cca50e604cfa9b4e3ae1d64b50ca1ae5b754122396084599ef903/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cdcb35dc9d807259c981a85531048ede628eabcffb3239adf3d17463518992d", size = 1456244, upload-time = "2026-03-09T13:15:20.444Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/7a/72c187abc6975f6978c3e39b7cf67aeb8b3c0a8f9790aa7fd412855e9e1f/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_39_riscv64.whl", hash = "sha256:70d593af6a6ca332d1df73d519fddb5148edb15cd90d5f0155e3746a6d4fcc65", size = 1073154, upload-time = "2026-03-09T13:15:22.039Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/ca/cf5b25783ebbd59143b4371ed0c8428a278abe68d6d0104b01865b1bbd0f/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:377815a8616074cabbf3f53354e1d040c35815a134e01d7614b7692e4bf8acfa", size = 2334377, upload-time = "2026-03-09T13:15:23.741Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/e5/b1f492adc516796e88751282276745340e2a72dcd0d36cf7173e0daf3210/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0255a027391d52944eae1dbb5d4cc5903f57092f3674e8e544cdd2622826b3f0", size = 2425288, upload-time = "2026-03-09T13:15:25.789Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/e5/9b21fbe91a61b8f409d74a26498706e97a48008bfcd1864373d32a6ba31c/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:012b1eb16e28718fa782b5e61dc6f2da1f0792ca73bd05d54de6cb9561665fc9", size = 2063158, upload-time = "2026-03-09T13:15:27.63Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/02/83f47986138310f95ea95531f851b2a62227c11cbc3e690ae1374fe49f0f/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0e3aafb33aed7479377e5e9a82e9d4bf87063741fc99fc7ae48b0f16e32bdd6f", size = 2597260, upload-time = "2026-03-09T13:15:29.421Z" },
+ { url = "https://files.pythonhosted.org/packages/07/18/43a5f24608d8c313dd189cf838c8e68d75b115567c6279de7796197cfb6a/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e7a116ae737f0000343218c4edf5bd45893bfeaff0993c0b215d7124c9f77646", size = 2394403, upload-time = "2026-03-09T13:15:31.517Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/b5/98222136d839b8afabcaa943b09bd05888c2d36355b7e448550211d1fca4/kiwisolver-1.5.0-cp314-cp314t-win_amd64.whl", hash = "sha256:1dd9b0b119a350976a6d781e7278ec7aca0b201e1a9e2d23d9804afecb6ca681", size = 79687, upload-time = "2026-03-09T13:15:33.204Z" },
+ { url = "https://files.pythonhosted.org/packages/99/a2/ca7dc962848040befed12732dff6acae7fb3c4f6fc4272b3f6c9a30b8713/kiwisolver-1.5.0-cp314-cp314t-win_arm64.whl", hash = "sha256:58f812017cd2985c21fbffb4864d59174d4903dd66fa23815e74bbc7a0e2dd57", size = 70032, upload-time = "2026-03-09T13:15:34.411Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/fa/2910df836372d8761bb6eff7d8bdcb1613b5c2e03f260efe7abe34d388a7/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-macosx_10_13_x86_64.whl", hash = "sha256:5ae8e62c147495b01a0f4765c878e9bfdf843412446a247e28df59936e99e797", size = 130262, upload-time = "2026-03-09T13:15:35.629Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/41/c5f71f9f00aabcc71fee8b7475e3f64747282580c2fe748961ba29b18385/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:f6764a4ccab3078db14a632420930f6186058750df066b8ea2a7106df91d3203", size = 138036, upload-time = "2026-03-09T13:15:36.894Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/06/7399a607f434119c6e1fdc8ec89a8d51ccccadf3341dee4ead6bd14caaf5/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c31c13da98624f957b0fb1b5bae5383b2333c2c3f6793d9825dd5ce79b525cb7", size = 194295, upload-time = "2026-03-09T13:15:38.22Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/91/53255615acd2a1eaca307ede3c90eb550bae9c94581f8c00081b6b1c8f44/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-win_amd64.whl", hash = "sha256:1f1489f769582498610e015a8ef2d36f28f505ab3096d0e16b4858a9ec214f57", size = 75987, upload-time = "2026-03-09T13:15:39.65Z" },
+ { url = "https://files.pythonhosted.org/packages/e9/eb/5fcbbbf9a0e2c3a35effb88831a483345326bbc3a030a3b5b69aee647f84/kiwisolver-1.5.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ec4c85dc4b687c7f7f15f553ff26a98bfe8c58f5f7f0ac8905f0ba4c7be60232", size = 59532, upload-time = "2026-03-09T13:15:47.047Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/9b/e17104555bb4db148fd52327feea1e96be4b88e8e008b029002c281a21ab/kiwisolver-1.5.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:12e91c215a96e39f57989c8912ae761286ac5a9584d04030ceb3368a357f017a", size = 57420, upload-time = "2026-03-09T13:15:48.199Z" },
+ { url = "https://files.pythonhosted.org/packages/48/44/2b5b95b7aa39fb2d8d9d956e0f3d5d45aef2ae1d942d4c3ffac2f9cfed1a/kiwisolver-1.5.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:be4a51a55833dc29ab5d7503e7bcb3b3af3402d266018137127450005cdfe737", size = 79892, upload-time = "2026-03-09T13:15:49.694Z" },
+ { url = "https://files.pythonhosted.org/packages/52/7d/7157f9bba6b455cfb4632ed411e199fc8b8977642c2b12082e1bd9e6d173/kiwisolver-1.5.0-pp311-pypy311_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:daae526907e262de627d8f70058a0f64acc9e2641c164c99c8f594b34a799a16", size = 77603, upload-time = "2026-03-09T13:15:50.945Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/dd/8050c947d435c8d4bc94e3252f4d8bb8a76cfb424f043a8680be637a57f1/kiwisolver-1.5.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:59cd8683f575d96df5bb48f6add94afc055012c29e28124fcae2b63661b9efb1", size = 73558, upload-time = "2026-03-09T13:15:52.112Z" },
]
[[package]]
name = "lazy-loader"
-version = "0.4"
-source = { registry = "https://pypi.org/simple" }
+version = "0.5"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
dependencies = [
{ name = "packaging" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/6f/6b/c875b30a1ba490860c93da4cabf479e03f584eba06fe5963f6f6644653d8/lazy_loader-0.4.tar.gz", hash = "sha256:47c75182589b91a4e1a85a136c074285a5ad4d9f39c63e0d7fb76391c4574cd1", size = 15431, upload-time = "2024-04-05T13:03:12.261Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/49/ac/21a1f8aa3777f5658576777ea76bfb124b702c520bbe90edf4ae9915eafa/lazy_loader-0.5.tar.gz", hash = "sha256:717f9179a0dbed357012ddad50a5ad3d5e4d9a0b8712680d4e687f5e6e6ed9b3", size = 15294, upload-time = "2026-03-06T15:45:09.054Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/83/60/d497a310bde3f01cb805196ac61b7ad6dc5dcf8dce66634dc34364b20b4f/lazy_loader-0.4-py3-none-any.whl", hash = "sha256:342aa8e14d543a154047afb4ba8ef17f5563baad3fc610d7b15b213b0f119efc", size = 12097, upload-time = "2024-04-05T13:03:10.514Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/a1/8d812e53a5da1687abb10445275d41a8b13adb781bbf7196ddbcf8d88505/lazy_loader-0.5-py3-none-any.whl", hash = "sha256:ab0ea149e9c554d4ffeeb21105ac60bed7f3b4fd69b1d2360a4add51b170b005", size = 8044, upload-time = "2026-03-06T15:45:07.668Z" },
]
[[package]]
name = "llvmlite"
-version = "0.45.1"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/99/8d/5baf1cef7f9c084fb35a8afbde88074f0d6a727bc63ef764fe0e7543ba40/llvmlite-0.45.1.tar.gz", hash = "sha256:09430bb9d0bb58fc45a45a57c7eae912850bedc095cd0810a57de109c69e1c32", size = 185600, upload-time = "2025-10-01T17:59:52.046Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/04/ad/9bdc87b2eb34642c1cfe6bcb4f5db64c21f91f26b010f263e7467e7536a3/llvmlite-0.45.1-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:60f92868d5d3af30b4239b50e1717cb4e4e54f6ac1c361a27903b318d0f07f42", size = 43043526, upload-time = "2025-10-01T18:03:15.051Z" },
- { url = "https://files.pythonhosted.org/packages/a5/ea/c25c6382f452a943b4082da5e8c1665ce29a62884e2ec80608533e8e82d5/llvmlite-0.45.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:98baab513e19beb210f1ef39066288784839a44cd504e24fff5d17f1b3cf0860", size = 37253118, upload-time = "2025-10-01T18:04:06.783Z" },
- { url = "https://files.pythonhosted.org/packages/fe/af/85fc237de98b181dbbe8647324331238d6c52a3554327ccdc83ced28efba/llvmlite-0.45.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3adc2355694d6a6fbcc024d59bb756677e7de506037c878022d7b877e7613a36", size = 56288209, upload-time = "2025-10-01T18:01:00.168Z" },
- { url = "https://files.pythonhosted.org/packages/0a/df/3daf95302ff49beff4230065e3178cd40e71294968e8d55baf4a9e560814/llvmlite-0.45.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2f3377a6db40f563058c9515dedcc8a3e562d8693a106a28f2ddccf2c8fcf6ca", size = 55140958, upload-time = "2025-10-01T18:02:11.199Z" },
- { url = "https://files.pythonhosted.org/packages/a4/56/4c0d503fe03bac820ecdeb14590cf9a248e120f483bcd5c009f2534f23f0/llvmlite-0.45.1-cp311-cp311-win_amd64.whl", hash = "sha256:f9c272682d91e0d57f2a76c6d9ebdfccc603a01828cdbe3d15273bdca0c3363a", size = 38132232, upload-time = "2025-10-01T18:04:52.181Z" },
- { url = "https://files.pythonhosted.org/packages/e2/7c/82cbd5c656e8991bcc110c69d05913be2229302a92acb96109e166ae31fb/llvmlite-0.45.1-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:28e763aba92fe9c72296911e040231d486447c01d4f90027c8e893d89d49b20e", size = 43043524, upload-time = "2025-10-01T18:03:30.666Z" },
- { url = "https://files.pythonhosted.org/packages/9d/bc/5314005bb2c7ee9f33102c6456c18cc81745d7055155d1218f1624463774/llvmlite-0.45.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1a53f4b74ee9fd30cb3d27d904dadece67a7575198bd80e687ee76474620735f", size = 37253123, upload-time = "2025-10-01T18:04:18.177Z" },
- { url = "https://files.pythonhosted.org/packages/96/76/0f7154952f037cb320b83e1c952ec4a19d5d689cf7d27cb8a26887d7bbc1/llvmlite-0.45.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b3796b1b1e1c14dcae34285d2f4ea488402fbd2c400ccf7137603ca3800864f", size = 56288211, upload-time = "2025-10-01T18:01:24.079Z" },
- { url = "https://files.pythonhosted.org/packages/00/b1/0b581942be2683ceb6862d558979e87387e14ad65a1e4db0e7dd671fa315/llvmlite-0.45.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:779e2f2ceefef0f4368548685f0b4adde34e5f4b457e90391f570a10b348d433", size = 55140958, upload-time = "2025-10-01T18:02:30.482Z" },
- { url = "https://files.pythonhosted.org/packages/33/94/9ba4ebcf4d541a325fd8098ddc073b663af75cc8b065b6059848f7d4dce7/llvmlite-0.45.1-cp312-cp312-win_amd64.whl", hash = "sha256:9e6c9949baf25d9aa9cd7cf0f6d011b9ca660dd17f5ba2b23bdbdb77cc86b116", size = 38132231, upload-time = "2025-10-01T18:05:03.664Z" },
- { url = "https://files.pythonhosted.org/packages/1d/e2/c185bb7e88514d5025f93c6c4092f6120c6cea8fe938974ec9860fb03bbb/llvmlite-0.45.1-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:d9ea9e6f17569a4253515cc01dade70aba536476e3d750b2e18d81d7e670eb15", size = 43043524, upload-time = "2025-10-01T18:03:43.249Z" },
- { url = "https://files.pythonhosted.org/packages/09/b8/b5437b9ecb2064e89ccf67dccae0d02cd38911705112dd0dcbfa9cd9a9de/llvmlite-0.45.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:c9f3cadee1630ce4ac18ea38adebf2a4f57a89bd2740ce83746876797f6e0bfb", size = 37253121, upload-time = "2025-10-01T18:04:30.557Z" },
- { url = "https://files.pythonhosted.org/packages/f7/97/ad1a907c0173a90dd4df7228f24a3ec61058bc1a9ff8a0caec20a0cc622e/llvmlite-0.45.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:57c48bf2e1083eedbc9406fb83c4e6483017879714916fe8be8a72a9672c995a", size = 56288210, upload-time = "2025-10-01T18:01:40.26Z" },
- { url = "https://files.pythonhosted.org/packages/32/d8/c99c8ac7a326e9735401ead3116f7685a7ec652691aeb2615aa732b1fc4a/llvmlite-0.45.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3aa3dfceda4219ae39cf18806c60eeb518c1680ff834b8b311bd784160b9ce40", size = 55140957, upload-time = "2025-10-01T18:02:46.244Z" },
- { url = "https://files.pythonhosted.org/packages/09/56/ed35668130e32dbfad2eb37356793b0a95f23494ab5be7d9bf5cb75850ee/llvmlite-0.45.1-cp313-cp313-win_amd64.whl", hash = "sha256:080e6f8d0778a8239cd47686d402cb66eb165e421efa9391366a9b7e5810a38b", size = 38132232, upload-time = "2025-10-01T18:05:14.477Z" },
+version = "0.47.0"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/01/88/a8952b6d5c21e74cbf158515b779666f692846502623e9e3c39d8e8ba25f/llvmlite-0.47.0.tar.gz", hash = "sha256:62031ce968ec74e95092184d4b0e857e444f8fdff0b8f9213707699570c33ccc", size = 193614, upload-time = "2026-03-31T18:29:53.497Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/34/0b/b9d1911cfefa61399821dfb37f486d83e0f42630a8d12f7194270c417002/llvmlite-0.47.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:74090f0dcfd6f24ebbef3f21f11e38111c4d7e6919b54c4416e1e357c3446b07", size = 37232770, upload-time = "2026-03-31T18:28:26.765Z" },
+ { url = "https://files.pythonhosted.org/packages/46/27/5799b020e4cdfb25a7c951c06a96397c135efcdc21b78d853bbd9c814c7d/llvmlite-0.47.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ca14f02e29134e837982497959a8e2193d6035235de1cb41a9cb2bd6da4eedbb", size = 56275177, upload-time = "2026-03-31T18:28:31.01Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/51/48a53fedf01cb1f3f43ef200be17ebf83c8d9a04018d3783c1a226c342c2/llvmlite-0.47.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:12a69d4bb05f402f30477e21eeabe81911e7c251cecb192bed82cd83c9db10d8", size = 55128631, upload-time = "2026-03-31T18:28:36.046Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/50/59227d06bdc96e23322713c381af4e77420949d8cd8a042c79e0043096cc/llvmlite-0.47.0-cp311-cp311-win_amd64.whl", hash = "sha256:c37d6eb7aaabfa83ab9c2ff5b5cdb95a5e6830403937b2c588b7490724e05327", size = 38138400, upload-time = "2026-03-31T18:28:40.076Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/48/4b7fe0e34c169fa2f12532916133e0b219d2823b540733651b34fdac509a/llvmlite-0.47.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:306a265f408c259067257a732c8e159284334018b4083a9e35f67d19792b164f", size = 37232769, upload-time = "2026-03-31T18:28:43.735Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/4b/e3f2cd17822cf772a4a51a0a8080b0032e6d37b2dbe8cfb724eac4e31c52/llvmlite-0.47.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5853bf26160857c0c2573415ff4efe01c4c651e59e2c55c2a088740acfee51cd", size = 56275178, upload-time = "2026-03-31T18:28:48.342Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/55/a3b4a543185305a9bdf3d9759d53646ed96e55e7dfd43f53e7a421b8fbae/llvmlite-0.47.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:003bcf7fa579e14db59c1a1e113f93ab8a06b56a4be31c7f08264d1d4072d077", size = 55128632, upload-time = "2026-03-31T18:28:52.901Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/f5/d281ae0f79378a5a91f308ea9fdb9f9cc068fddd09629edc0725a5a8fde1/llvmlite-0.47.0-cp312-cp312-win_amd64.whl", hash = "sha256:f3079f25bdc24cd9d27c4b2b5e68f5f60c4fdb7e8ad5ee2b9b006007558f9df7", size = 38138692, upload-time = "2026-03-31T18:28:57.147Z" },
+ { url = "https://files.pythonhosted.org/packages/77/6f/4615353e016799f80fa52ccb270a843c413b22361fadda2589b2922fb9b0/llvmlite-0.47.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:a3c6a735d4e1041808434f9d440faa3d78d9b4af2ee64d05a66f351883b6ceec", size = 37232771, upload-time = "2026-03-31T18:29:01.324Z" },
+ { url = "https://files.pythonhosted.org/packages/31/b8/69f5565f1a280d032525878a86511eebed0645818492feeb169dfb20ae8e/llvmlite-0.47.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2699a74321189e812d476a43d6d7f652f51811e7b5aad9d9bba842a1c7927acb", size = 56275178, upload-time = "2026-03-31T18:29:05.748Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/da/b32cafcb926fb0ce2aa25553bf32cb8764af31438f40e2481df08884c947/llvmlite-0.47.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6c6951e2b29930227963e53ee152441f0e14be92e9d4231852102d986c761e40", size = 55128632, upload-time = "2026-03-31T18:29:11.235Z" },
+ { url = "https://files.pythonhosted.org/packages/46/9f/4898b44e4042c60fafcb1162dfb7014f6f15b1ec19bf29cfea6bf26df90d/llvmlite-0.47.0-cp313-cp313-win_amd64.whl", hash = "sha256:c2e9adf8698d813a9a5efb2d4370caf344dbc1e145019851fee6a6f319ba760e", size = 38138695, upload-time = "2026-03-31T18:29:15.43Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/d4/33c8af00f0bf6f552d74f3a054f648af2c5bc6bece97972f3bfadce4f5ec/llvmlite-0.47.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:de966c626c35c9dff5ae7bf12db25637738d0df83fc370cf793bc94d43d92d14", size = 37232773, upload-time = "2026-03-31T18:29:19.453Z" },
+ { url = "https://files.pythonhosted.org/packages/64/1d/a760e993e0c0ba6db38d46b9f48f6c7dceb8ac838824997fb9e25f97bc04/llvmlite-0.47.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ddbccff2aeaff8670368340a158abefc032fe9b3ccf7d9c496639263d00151aa", size = 56275176, upload-time = "2026-03-31T18:29:24.149Z" },
+ { url = "https://files.pythonhosted.org/packages/84/3b/e679bc3b29127182a7f4aa2d2e9e5bea42adb93fb840484147d59c236299/llvmlite-0.47.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4a7b778a2e144fc64468fb9bf509ac1226c9813a00b4d7afea5d988c4e22fca", size = 55128631, upload-time = "2026-03-31T18:29:29.536Z" },
+ { url = "https://files.pythonhosted.org/packages/be/f7/19e2a09c62809c9e63bbd14ce71fb92c6ff7b7b3045741bb00c781efc3c9/llvmlite-0.47.0-cp314-cp314-win_amd64.whl", hash = "sha256:694e3c2cdc472ed2bd8bd4555ca002eec4310961dd58ef791d508f57b5cc4c94", size = 39153826, upload-time = "2026-03-31T18:29:33.681Z" },
+ { url = "https://files.pythonhosted.org/packages/40/a1/581a8c707b5e80efdbbe1dd94527404d33fe50bceb71f39d5a7e11bd57b7/llvmlite-0.47.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:92ec8a169a20b473c1c54d4695e371bde36489fc1efa3688e11e99beba0abf9c", size = 37232772, upload-time = "2026-03-31T18:29:37.952Z" },
+ { url = "https://files.pythonhosted.org/packages/11/03/16090dd6f74ba2b8b922276047f15962fbeea0a75d5601607edb301ba945/llvmlite-0.47.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fa1cbd800edd3b20bc141521f7fd45a6185a5b84109aa6855134e81397ffe72b", size = 56275178, upload-time = "2026-03-31T18:29:42.58Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/cb/0abf1dd4c5286a95ffe0c1d8c67aec06b515894a0dd2ac97f5e27b82ab0b/llvmlite-0.47.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f6725179b89f03b17dabe236ff3422cb8291b4c1bf40af152826dfd34e350ae8", size = 55128632, upload-time = "2026-03-31T18:29:46.939Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/79/d3bbab197e86e0ff4f9c07122895b66a3e0d024247fcff7f12c473cb36d9/llvmlite-0.47.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6842cf6f707ec4be3d985a385ad03f72b2d724439e118fcbe99b2929964f0453", size = 39153839, upload-time = "2026-03-31T18:29:51.004Z" },
]
[[package]]
name = "markdown-it-py"
version = "4.0.0"
-source = { registry = "https://pypi.org/simple" }
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
dependencies = [
{ name = "mdurl" },
]
@@ -586,10 +659,19 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" },
]
+[[package]]
+name = "markdown2"
+version = "2.5.5"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/e4/ae/07d4a5fcaa5509221287d289323d75ac8eda5a5a4ac9de2accf7bbcc2b88/markdown2-2.5.5.tar.gz", hash = "sha256:001547e68f6e7fcf0f1cb83f7e82f48aa7d48b2c6a321f0cd20a853a8a2d1664", size = 157249, upload-time = "2026-03-02T20:46:53.411Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/43/af/4b3891eb0a49d6cfd5cbf3e9bf514c943afc2b0f13e2c57cc57cd88ecc21/markdown2-2.5.5-py3-none-any.whl", hash = "sha256:be798587e09d1f52d2e4d96a649c4b82a778c75f9929aad52a2c95747fa26941", size = 56250, upload-time = "2026-03-02T20:46:52.032Z" },
+]
+
[[package]]
name = "markupsafe"
version = "3.0.3"
-source = { registry = "https://pypi.org/simple" }
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/08/db/fefacb2136439fc8dd20e797950e749aa1f4997ed584c62cfb8ef7c2be0e/markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad", size = 11631, upload-time = "2025-09-27T18:36:18.185Z" },
@@ -663,7 +745,7 @@ wheels = [
[[package]]
name = "matplotlib"
version = "3.8.0"
-source = { registry = "https://pypi.org/simple" }
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
dependencies = [
{ name = "contourpy" },
{ name = "cycler" },
@@ -694,7 +776,7 @@ wheels = [
[[package]]
name = "mdurl"
version = "0.1.2"
-source = { registry = "https://pypi.org/simple" }
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" },
@@ -703,7 +785,7 @@ wheels = [
[[package]]
name = "ml-dtypes"
version = "0.4.1"
-source = { registry = "https://pypi.org/simple" }
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
dependencies = [
{ name = "numpy" },
]
@@ -722,7 +804,7 @@ wheels = [
[[package]]
name = "mpmath"
version = "1.3.0"
-source = { registry = "https://pypi.org/simple" }
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload-time = "2023-03-07T16:47:11.061Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" },
@@ -731,7 +813,7 @@ wheels = [
[[package]]
name = "natsort"
version = "8.4.0"
-source = { registry = "https://pypi.org/simple" }
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/e2/a9/a0c57aee75f77794adaf35322f8b6404cbd0f89ad45c87197a937764b7d0/natsort-8.4.0.tar.gz", hash = "sha256:45312c4a0e5507593da193dedd04abb1469253b601ecaf63445ad80f0a1ea581", size = 76575, upload-time = "2023-06-20T04:17:19.925Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ef/82/7a9d0550484a62c6da82858ee9419f3dd1ccc9aa1c26a1e43da3ecd20b0d/natsort-8.4.0-py3-none-any.whl", hash = "sha256:4732914fb471f56b5cce04d7bae6f164a592c7712e1c85f9ef585e197299521c", size = 38268, upload-time = "2023-06-20T04:17:17.522Z" },
@@ -739,44 +821,49 @@ wheels = [
[[package]]
name = "networkx"
-version = "3.5"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/6c/4f/ccdb8ad3a38e583f214547fd2f7ff1fc160c43a75af88e6aec213404b96a/networkx-3.5.tar.gz", hash = "sha256:d4c6f9cf81f52d69230866796b82afbccdec3db7ae4fbd1b65ea750feed50037", size = 2471065, upload-time = "2025-05-29T11:35:07.804Z" }
+version = "3.6.1"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/6a/51/63fe664f3908c97be9d2e4f1158eb633317598cfa6e1fc14af5383f17512/networkx-3.6.1.tar.gz", hash = "sha256:26b7c357accc0c8cde558ad486283728b65b6a95d85ee1cd66bafab4c8168509", size = 2517025, upload-time = "2025-12-08T17:02:39.908Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/eb/8d/776adee7bbf76365fdd7f2552710282c79a4ead5d2a46408c9043a2b70ba/networkx-3.5-py3-none-any.whl", hash = "sha256:0030d386a9a06dee3565298b4a734b68589749a544acbb6c412dc9e2489ec6ec", size = 2034406, upload-time = "2025-05-29T11:35:04.961Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl", hash = "sha256:d47fbf302e7d9cbbb9e2555a0d267983d2aa476bac30e90dfbe5669bd57f3762", size = 2068504, upload-time = "2025-12-08T17:02:38.159Z" },
]
[[package]]
name = "numba"
-version = "0.62.1"
-source = { registry = "https://pypi.org/simple" }
+version = "0.65.0"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
dependencies = [
{ name = "llvmlite" },
{ name = "numpy" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/a3/20/33dbdbfe60e5fd8e3dbfde299d106279a33d9f8308346022316781368591/numba-0.62.1.tar.gz", hash = "sha256:7b774242aa890e34c21200a1fc62e5b5757d5286267e71103257f4e2af0d5161", size = 2749817, upload-time = "2025-09-29T10:46:31.551Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/dd/5f/8b3491dd849474f55e33c16ef55678ace1455c490555337899c35826836c/numba-0.62.1-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:f43e24b057714e480fe44bc6031de499e7cf8150c63eb461192caa6cc8530bc8", size = 2684279, upload-time = "2025-09-29T10:43:37.213Z" },
- { url = "https://files.pythonhosted.org/packages/bf/18/71969149bfeb65a629e652b752b80167fe8a6a6f6e084f1f2060801f7f31/numba-0.62.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:57cbddc53b9ee02830b828a8428757f5c218831ccc96490a314ef569d8342b7b", size = 2687330, upload-time = "2025-09-29T10:43:59.601Z" },
- { url = "https://files.pythonhosted.org/packages/0e/7d/403be3fecae33088027bc8a95dc80a2fda1e3beff3e0e5fc4374ada3afbe/numba-0.62.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:604059730c637c7885386521bb1b0ddcbc91fd56131a6dcc54163d6f1804c872", size = 3739727, upload-time = "2025-09-29T10:42:45.922Z" },
- { url = "https://files.pythonhosted.org/packages/e0/c3/3d910d08b659a6d4c62ab3cd8cd93c4d8b7709f55afa0d79a87413027ff6/numba-0.62.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d6c540880170bee817011757dc9049dba5a29db0c09b4d2349295991fe3ee55f", size = 3445490, upload-time = "2025-09-29T10:43:12.692Z" },
- { url = "https://files.pythonhosted.org/packages/5b/82/9d425c2f20d9f0a37f7cb955945a553a00fa06a2b025856c3550227c5543/numba-0.62.1-cp311-cp311-win_amd64.whl", hash = "sha256:03de6d691d6b6e2b76660ba0f38f37b81ece8b2cc524a62f2a0cfae2bfb6f9da", size = 2745550, upload-time = "2025-09-29T10:44:20.571Z" },
- { url = "https://files.pythonhosted.org/packages/5e/fa/30fa6873e9f821c0ae755915a3ca444e6ff8d6a7b6860b669a3d33377ac7/numba-0.62.1-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:1b743b32f8fa5fff22e19c2e906db2f0a340782caf024477b97801b918cf0494", size = 2685346, upload-time = "2025-09-29T10:43:43.677Z" },
- { url = "https://files.pythonhosted.org/packages/a9/d5/504ce8dc46e0dba2790c77e6b878ee65b60fe3e7d6d0006483ef6fde5a97/numba-0.62.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:90fa21b0142bcf08ad8e32a97d25d0b84b1e921bc9423f8dda07d3652860eef6", size = 2688139, upload-time = "2025-09-29T10:44:04.894Z" },
- { url = "https://files.pythonhosted.org/packages/50/5f/6a802741176c93f2ebe97ad90751894c7b0c922b52ba99a4395e79492205/numba-0.62.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6ef84d0ac19f1bf80431347b6f4ce3c39b7ec13f48f233a48c01e2ec06ecbc59", size = 3796453, upload-time = "2025-09-29T10:42:52.771Z" },
- { url = "https://files.pythonhosted.org/packages/7e/df/efd21527d25150c4544eccc9d0b7260a5dec4b7e98b5a581990e05a133c0/numba-0.62.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9315cc5e441300e0ca07c828a627d92a6802bcbf27c5487f31ae73783c58da53", size = 3496451, upload-time = "2025-09-29T10:43:19.279Z" },
- { url = "https://files.pythonhosted.org/packages/80/44/79bfdab12a02796bf4f1841630355c82b5a69933b1d50eb15c7fa37dabe8/numba-0.62.1-cp312-cp312-win_amd64.whl", hash = "sha256:44e3aa6228039992f058f5ebfcfd372c83798e9464297bdad8cc79febcf7891e", size = 2745552, upload-time = "2025-09-29T10:44:26.399Z" },
- { url = "https://files.pythonhosted.org/packages/22/76/501ea2c07c089ef1386868f33dff2978f43f51b854e34397b20fc55e0a58/numba-0.62.1-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:b72489ba8411cc9fdcaa2458d8f7677751e94f0109eeb53e5becfdc818c64afb", size = 2685766, upload-time = "2025-09-29T10:43:49.161Z" },
- { url = "https://files.pythonhosted.org/packages/80/68/444986ed95350c0611d5c7b46828411c222ce41a0c76707c36425d27ce29/numba-0.62.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:44a1412095534a26fb5da2717bc755b57da5f3053965128fe3dc286652cc6a92", size = 2688741, upload-time = "2025-09-29T10:44:10.07Z" },
- { url = "https://files.pythonhosted.org/packages/78/7e/bf2e3634993d57f95305c7cee4c9c6cb3c9c78404ee7b49569a0dfecfe33/numba-0.62.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8c9460b9e936c5bd2f0570e20a0a5909ee6e8b694fd958b210e3bde3a6dba2d7", size = 3804576, upload-time = "2025-09-29T10:42:59.53Z" },
- { url = "https://files.pythonhosted.org/packages/e8/b6/8a1723fff71f63bbb1354bdc60a1513a068acc0f5322f58da6f022d20247/numba-0.62.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:728f91a874192df22d74e3fd42c12900b7ce7190b1aad3574c6c61b08313e4c5", size = 3503367, upload-time = "2025-09-29T10:43:26.326Z" },
- { url = "https://files.pythonhosted.org/packages/9c/ec/9d414e7a80d6d1dc4af0e07c6bfe293ce0b04ea4d0ed6c45dad9bd6e72eb/numba-0.62.1-cp313-cp313-win_amd64.whl", hash = "sha256:bbf3f88b461514287df66bc8d0307e949b09f2b6f67da92265094e8fa1282dd8", size = 2745529, upload-time = "2025-09-29T10:44:31.738Z" },
+sdist = { url = "https://files.pythonhosted.org/packages/49/61/7299643b9c18d669e04be7c5bcb64d985070d07553274817b45b049e7bfe/numba-0.65.0.tar.gz", hash = "sha256:edad0d9f6682e93624c00125a471ae4df186175d71fd604c983c377cdc03e68b", size = 2764131, upload-time = "2026-04-01T03:52:01.946Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ba/ce/d67c499703eb5479ce02420e8ccd65c5753d87d2e16d563f152d71405346/numba-0.65.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:28e547d0b18024f19cbaf9de02fc5c145790213d9be8a2c95b43f93ec162b9e4", size = 2680228, upload-time = "2026-04-01T03:51:25.401Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/a7/11e2b24251d57cf41fc9ad83f378d890d61a890e3f8eb6338b39833f67a4/numba-0.65.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:032b0b8e879512cd424d79eed6d772a1399c6387ded184c2cf3cc22c08d750a6", size = 3744674, upload-time = "2026-04-01T03:51:27.311Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/0b/7c63eb742859a6243f42288441f65ac9dac96ea59f409e43b713aafbe867/numba-0.65.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:af143d823624033a128b5950c0aaf9ffc2386dfe954eb757119cf0432335534c", size = 3450620, upload-time = "2026-04-01T03:51:29.092Z" },
+ { url = "https://files.pythonhosted.org/packages/53/ff/1371cbbe955be340a46093a10b61462437e0fadc7a63290473a0e584cb03/numba-0.65.0-cp311-cp311-win_amd64.whl", hash = "sha256:15d159578e59a39df246b83480f78d7794b0fca40153b5684d3849a99c48a0fb", size = 2747081, upload-time = "2026-04-01T03:51:30.785Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/2f/8bd31a1ea43c01ac215283d83aa5f8d5acbe7a36c85b82f1757bfe9ccb31/numba-0.65.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:b27ee4847e1bfb17e9604d100417ee7c1d10f15a6711c6213404b3da13a0b2aa", size = 2680705, upload-time = "2026-04-01T03:51:32.597Z" },
+ { url = "https://files.pythonhosted.org/packages/73/36/88406bd58600cc696417b8e5dd6a056478da808f3eaf48d18e2421e0c2d9/numba-0.65.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a52d92ffd297c10364bce60cd1fcb88f99284ab5df085f2c6bcd1cb33b529a6f", size = 3801411, upload-time = "2026-04-01T03:51:34.321Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/61/ce753a1d7646dd477e16d15e89473703faebb8995d2f71d7ad69a540b565/numba-0.65.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:da8e371e328c06d0010c3d8b44b21858652831b85bcfba78cb22c042e22dbd8e", size = 3501622, upload-time = "2026-04-01T03:51:36.348Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/86/db87a5393f1b1fabef53ac3ba4e6b938bb27e40a04ad7cc512098fcae032/numba-0.65.0-cp312-cp312-win_amd64.whl", hash = "sha256:59bb9f2bb9f1238dfd8e927ba50645c18ae769fef4f3d58ea0ea22a2683b91f5", size = 2749979, upload-time = "2026-04-01T03:51:37.88Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/f8/eee0f1ff456218db036bfc9023995ec1f85a9dc8f2422f1594f6a87829e0/numba-0.65.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:c6334094563a456a695c812e6846288376ca02327cf246cdcc83e1bb27862367", size = 2680679, upload-time = "2026-04-01T03:51:39.491Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/8f/3d116e4b8e92f6abace431afa4b2b944f4d65bdee83af886f5c4b263df95/numba-0.65.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b8a9008411615c69d083d1dcf477f75a5aa727b30beb16e139799e2be945cdfd", size = 3809537, upload-time = "2026-04-01T03:51:41.42Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/2c/6a3ca4128e253cb67affe06deb47688f51ce968f5111e2a06d010e6f1fa6/numba-0.65.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:af96c0cba53664efcb361528b8c75e011a6556c859c7e08424c2715201c6cf7a", size = 3508615, upload-time = "2026-04-01T03:51:43.444Z" },
+ { url = "https://files.pythonhosted.org/packages/96/0e/267f9a36fb282c104a971d7eecb685b411c47dce2a740fe69cf5fc2945d9/numba-0.65.0-cp313-cp313-win_amd64.whl", hash = "sha256:6254e73b9c929dc736a1fbd3d6f5680789709a5067cae1fa7198707385129c04", size = 2749938, upload-time = "2026-04-01T03:51:45.218Z" },
+ { url = "https://files.pythonhosted.org/packages/56/a4/90edb01e9176053578e343d7a7276bc28356741ee67059aed8ed2c1a4e59/numba-0.65.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:ee336b398a6fca51b1f626034de99f50cb1bd87d537a166275158a3cee744b82", size = 2680878, upload-time = "2026-04-01T03:51:46.91Z" },
+ { url = "https://files.pythonhosted.org/packages/24/8d/e12d6ff4b9119db3cbf7b2db1ce257576441bd3c76388c786dea74f20b02/numba-0.65.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:05c0a9fdf75d85f57dee47b719e8d6415707b80aae45d75f63f9dc1b935c29f7", size = 3778456, upload-time = "2026-04-01T03:51:48.552Z" },
+ { url = "https://files.pythonhosted.org/packages/17/89/abcd83e76f6a773276fe76244140671bcc5bf820f6e2ae1a15362ae4c8c9/numba-0.65.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:583680e0e8faf124d362df23b4b593f3221a8996341a63d1b664c122401bec2f", size = 3478464, upload-time = "2026-04-01T03:51:50.527Z" },
+ { url = "https://files.pythonhosted.org/packages/73/5b/fbce55ce3d933afbc7ade04df826853e4a846aaa47d58d2fbb669b8f2d08/numba-0.65.0-cp314-cp314-win_amd64.whl", hash = "sha256:add297d3e1c08dd884f44100152612fa41e66a51d15fdf91307f9dde31d06830", size = 2752012, upload-time = "2026-04-01T03:51:52.691Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/ab/af705f4257d9388fb2fd6d7416573e98b6ca9c786e8b58f02720978557bd/numba-0.65.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:194a243ba53a9157c8538cbb3166ec015d785a8c5d584d06cdd88bee902233c7", size = 2683961, upload-time = "2026-04-01T03:51:54.281Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/e5/8267b0adb0c01b52b553df5062fbbb42c30ed5362d08b85cc913a36f838f/numba-0.65.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c7fa502960f7a2f3f5cb025bc7bff888a3551277b92431bfdc5ba2f11a375749", size = 3816373, upload-time = "2026-04-01T03:51:56.18Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/f5/b8397ca360971669a93706b9274592b6864e4367a37d498fbbcb62aa2d48/numba-0.65.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5046c63f783ca3eb6195f826a50797465e7c4ce811daa17c9bea47e310c9b964", size = 3532782, upload-time = "2026-04-01T03:51:58.387Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/21/1e73fa16bf0393ebb74c5bb208d712152ffdfc84600a8e93a3180317856e/numba-0.65.0-cp314-cp314t-win_amd64.whl", hash = "sha256:46fd679ae4f68c7a5d5721efbd29ecee0b0f3013211591891d79b51bfdf73113", size = 2757611, upload-time = "2026-04-01T03:52:00.083Z" },
]
[[package]]
name = "numpy"
version = "1.26.4"
-source = { registry = "https://pypi.org/simple" }
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/65/6e/09db70a523a96d25e115e71cc56a6f9031e7b8cd166c1ac8438307c14058/numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010", size = 15786129, upload-time = "2024-02-06T00:26:44.495Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/11/57/baae43d14fe163fa0e4c47f307b6b2511ab8d7d30177c491960504252053/numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71", size = 20630554, upload-time = "2024-02-05T23:51:50.149Z" },
@@ -798,152 +885,167 @@ wheels = [
]
[[package]]
-name = "nvidia-cublas-cu12"
-version = "12.8.4.1"
-source = { registry = "https://pypi.org/simple" }
+name = "nvidia-cublas"
+version = "13.1.0.3"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/dc/61/e24b560ab2e2eaeb3c839129175fb330dfcfc29e5203196e5541a4c44682/nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:8ac4e771d5a348c551b2a426eda6193c19aa630236b418086020df5ba9667142", size = 594346921, upload-time = "2025-03-07T01:44:31.254Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/a5/fce49e2ae977e0ccc084e5adafceb4f0ac0c8333cb6863501618a7277f67/nvidia_cublas-13.1.0.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:c86fc7f7ae36d7528288c5d88098edcb7b02c633d262e7ddbb86b0ad91be5df2", size = 542851226, upload-time = "2025-10-09T08:59:04.818Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/44/423ac00af4dd95a5aeb27207e2c0d9b7118702149bf4704c3ddb55bb7429/nvidia_cublas-13.1.0.3-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:ee8722c1f0145ab246bccb9e452153b5e0515fd094c3678df50b2a0888b8b171", size = 423133236, upload-time = "2025-10-09T08:59:32.536Z" },
]
[[package]]
-name = "nvidia-cuda-cupti-cu12"
-version = "12.8.90"
-source = { registry = "https://pypi.org/simple" }
+name = "nvidia-cuda-cupti"
+version = "13.0.85"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/f8/02/2adcaa145158bf1a8295d83591d22e4103dbfd821bcaf6f3f53151ca4ffa/nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ea0cb07ebda26bb9b29ba82cda34849e73c166c18162d3913575b0c9db9a6182", size = 10248621, upload-time = "2025-03-07T01:40:21.213Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/2a/80353b103fc20ce05ef51e928daed4b6015db4aaa9162ed0997090fe2250/nvidia_cuda_cupti-13.0.85-py3-none-manylinux_2_25_aarch64.whl", hash = "sha256:796bd679890ee55fb14a94629b698b6db54bcfd833d391d5e94017dd9d7d3151", size = 10310827, upload-time = "2025-09-04T08:26:42.012Z" },
+ { url = "https://files.pythonhosted.org/packages/33/6d/737d164b4837a9bbd202f5ae3078975f0525a55730fe871d8ed4e3b952b0/nvidia_cuda_cupti-13.0.85-py3-none-manylinux_2_25_x86_64.whl", hash = "sha256:4eb01c08e859bf924d222250d2e8f8b8ff6d3db4721288cf35d14252a4d933c8", size = 10715597, upload-time = "2025-09-04T08:26:51.312Z" },
]
[[package]]
-name = "nvidia-cuda-nvrtc-cu12"
-version = "12.8.93"
-source = { registry = "https://pypi.org/simple" }
+name = "nvidia-cuda-nvrtc"
+version = "13.0.88"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/05/6b/32f747947df2da6994e999492ab306a903659555dddc0fbdeb9d71f75e52/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:a7756528852ef889772a84c6cd89d41dfa74667e24cca16bb31f8f061e3e9994", size = 88040029, upload-time = "2025-03-07T01:42:13.562Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/68/483a78f5e8f31b08fb1bb671559968c0ca3a065ac7acabfc7cee55214fd6/nvidia_cuda_nvrtc-13.0.88-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:ad9b6d2ead2435f11cbb6868809d2adeeee302e9bb94bcf0539c7a40d80e8575", size = 90215200, upload-time = "2025-09-04T08:28:44.204Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/dc/6bb80850e0b7edd6588d560758f17e0550893a1feaf436807d64d2da040f/nvidia_cuda_nvrtc-13.0.88-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d27f20a0ca67a4bb34268a5e951033496c5b74870b868bacd046b1b8e0c3267b", size = 43015449, upload-time = "2025-09-04T08:28:20.239Z" },
]
[[package]]
-name = "nvidia-cuda-runtime-cu12"
-version = "12.8.90"
-source = { registry = "https://pypi.org/simple" }
+name = "nvidia-cuda-runtime"
+version = "13.0.96"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/0d/9b/a997b638fcd068ad6e4d53b8551a7d30fe8b404d6f1804abf1df69838932/nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adade8dcbd0edf427b7204d480d6066d33902cab2a4707dcfc48a2d0fd44ab90", size = 954765, upload-time = "2025-03-07T01:40:01.615Z" },
+ { url = "https://files.pythonhosted.org/packages/87/4f/17d7b9b8e285199c58ce28e31b5c5bbaa4d8271af06a89b6405258245de2/nvidia_cuda_runtime-13.0.96-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ef9bcbe90493a2b9d810e43d249adb3d02e98dd30200d86607d8d02687c43f55", size = 2261060, upload-time = "2025-10-09T08:55:15.78Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/24/d1558f3b68b1d26e706813b1d10aa1d785e4698c425af8db8edc3dced472/nvidia_cuda_runtime-13.0.96-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7f82250d7782aa23b6cfe765ecc7db554bd3c2870c43f3d1821f1d18aebf0548", size = 2243632, upload-time = "2025-10-09T08:55:36.117Z" },
]
[[package]]
-name = "nvidia-cudnn-cu12"
-version = "9.10.2.21"
-source = { registry = "https://pypi.org/simple" }
+name = "nvidia-cudnn-cu13"
+version = "9.19.0.56"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
dependencies = [
- { name = "nvidia-cublas-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'linux' and sys_platform != 'win32')" },
+ { name = "nvidia-cublas", marker = "sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'" },
]
wheels = [
- { url = "https://files.pythonhosted.org/packages/ba/51/e123d997aa098c61d029f76663dedbfb9bc8dcf8c60cbd6adbe42f76d049/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8", size = 706758467, upload-time = "2025-06-06T21:54:08.597Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/84/26025437c1e6b61a707442184fa0c03d083b661adf3a3eecfd6d21677740/nvidia_cudnn_cu13-9.19.0.56-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:6ed29ffaee1176c612daf442e4dd6cfeb6a0caa43ddcbeb59da94953030b1be4", size = 433781201, upload-time = "2026-02-03T20:40:53.805Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/22/0b4b932655d17a6da1b92fa92ab12844b053bb2ac2475e179ba6f043da1e/nvidia_cudnn_cu13-9.19.0.56-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:d20e1734305e9d68889a96e3f35094d733ff1f83932ebe462753973e53a572bf", size = 366066321, upload-time = "2026-02-03T20:44:52.837Z" },
]
[[package]]
-name = "nvidia-cufft-cu12"
-version = "11.3.3.83"
-source = { registry = "https://pypi.org/simple" }
+name = "nvidia-cufft"
+version = "12.0.0.61"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
dependencies = [
- { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'linux' and sys_platform != 'win32')" },
+ { name = "nvidia-nvjitlink", marker = "sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'" },
]
wheels = [
- { url = "https://files.pythonhosted.org/packages/1f/13/ee4e00f30e676b66ae65b4f08cb5bcbb8392c03f54f2d5413ea99a5d1c80/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74", size = 193118695, upload-time = "2025-03-07T01:45:27.821Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/ae/f417a75c0259e85c1d2f83ca4e960289a5f814ed0cea74d18c353d3e989d/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2708c852ef8cd89d1d2068bdbece0aa188813a0c934db3779b9b1faa8442e5f5", size = 214053554, upload-time = "2025-09-04T08:31:38.196Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/2f/7b57e29836ea8714f81e9898409196f47d772d5ddedddf1592eadb8ab743/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6c44f692dce8fd5ffd3e3df134b6cdb9c2f72d99cf40b62c32dde45eea9ddad3", size = 214085489, upload-time = "2025-09-04T08:31:56.044Z" },
]
[[package]]
-name = "nvidia-cufile-cu12"
-version = "1.13.1.3"
-source = { registry = "https://pypi.org/simple" }
+name = "nvidia-cufile"
+version = "1.15.1.6"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/bb/fe/1bcba1dfbfb8d01be8d93f07bfc502c93fa23afa6fd5ab3fc7c1df71038a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1d069003be650e131b21c932ec3d8969c1715379251f8d23a1860554b1cb24fc", size = 1197834, upload-time = "2025-03-07T01:45:50.723Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/70/4f193de89a48b71714e74602ee14d04e4019ad36a5a9f20c425776e72cd6/nvidia_cufile-1.15.1.6-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:08a3ecefae5a01c7f5117351c64f17c7c62efa5fffdbe24fc7d298da19cd0b44", size = 1223672, upload-time = "2025-09-04T08:32:22.779Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/73/cc4a14c9813a8a0d509417cf5f4bdaba76e924d58beb9864f5a7baceefbf/nvidia_cufile-1.15.1.6-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:bdc0deedc61f548bddf7733bdc216456c2fdb101d020e1ab4b88d232d5e2f6d1", size = 1136992, upload-time = "2025-09-04T08:32:14.119Z" },
]
[[package]]
-name = "nvidia-curand-cu12"
-version = "10.3.9.90"
-source = { registry = "https://pypi.org/simple" }
+name = "nvidia-curand"
+version = "10.4.0.35"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/fb/aa/6584b56dc84ebe9cf93226a5cde4d99080c8e90ab40f0c27bda7a0f29aa1/nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:b32331d4f4df5d6eefa0554c565b626c7216f87a06a4f56fab27c3b68a830ec9", size = 63619976, upload-time = "2025-03-07T01:46:23.323Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/72/7c2ae24fb6b63a32e6ae5d241cc65263ea18d08802aaae087d9f013335a2/nvidia_curand-10.4.0.35-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:133df5a7509c3e292aaa2b477afd0194f06ce4ea24d714d616ff36439cee349a", size = 61962106, upload-time = "2025-08-04T10:21:41.128Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/9f/be0a41ca4a4917abf5cb9ae0daff1a6060cc5de950aec0396de9f3b52bc5/nvidia_curand-10.4.0.35-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:1aee33a5da6e1db083fe2b90082def8915f30f3248d5896bcec36a579d941bfc", size = 59544258, upload-time = "2025-08-04T10:22:03.992Z" },
]
[[package]]
-name = "nvidia-cusolver-cu12"
-version = "11.7.3.90"
-source = { registry = "https://pypi.org/simple" }
+name = "nvidia-cusolver"
+version = "12.0.4.66"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
dependencies = [
- { name = "nvidia-cublas-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'linux' and sys_platform != 'win32')" },
- { name = "nvidia-cusparse-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'linux' and sys_platform != 'win32')" },
- { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'linux' and sys_platform != 'win32')" },
+ { name = "nvidia-cublas", marker = "sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'" },
+ { name = "nvidia-cusparse", marker = "sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'" },
+ { name = "nvidia-nvjitlink", marker = "sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'" },
]
wheels = [
- { url = "https://files.pythonhosted.org/packages/85/48/9a13d2975803e8cf2777d5ed57b87a0b6ca2cc795f9a4f59796a910bfb80/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450", size = 267506905, upload-time = "2025-03-07T01:47:16.273Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/c3/b30c9e935fc01e3da443ec0116ed1b2a009bb867f5324d3f2d7e533e776b/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:02c2457eaa9e39de20f880f4bd8820e6a1cfb9f9a34f820eb12a155aa5bc92d2", size = 223467760, upload-time = "2025-09-04T08:33:04.222Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/67/cba3777620cdacb99102da4042883709c41c709f4b6323c10781a9c3aa34/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:0a759da5dea5c0ea10fd307de75cdeb59e7ea4fcb8add0924859b944babf1112", size = 200941980, upload-time = "2025-09-04T08:33:22.767Z" },
]
[[package]]
-name = "nvidia-cusparse-cu12"
-version = "12.5.8.93"
-source = { registry = "https://pypi.org/simple" }
+name = "nvidia-cusparse"
+version = "12.6.3.3"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
dependencies = [
- { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'linux' and sys_platform != 'win32')" },
+ { name = "nvidia-nvjitlink", marker = "sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'" },
]
wheels = [
- { url = "https://files.pythonhosted.org/packages/c2/f5/e1854cb2f2bcd4280c44736c93550cc300ff4b8c95ebe370d0aa7d2b473d/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b", size = 288216466, upload-time = "2025-03-07T01:48:13.779Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/94/5c26f33738ae35276672f12615a64bd008ed5be6d1ebcb23579285d960a9/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:80bcc4662f23f1054ee334a15c72b8940402975e0eab63178fc7e670aa59472c", size = 162155568, upload-time = "2025-09-04T08:33:42.864Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/18/623c77619c31d62efd55302939756966f3ecc8d724a14dab2b75f1508850/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2b3c89c88d01ee0e477cb7f82ef60a11a4bcd57b6b87c33f789350b59759360b", size = 145942937, upload-time = "2025-09-04T08:33:58.029Z" },
]
[[package]]
-name = "nvidia-cusparselt-cu12"
-version = "0.7.1"
-source = { registry = "https://pypi.org/simple" }
+name = "nvidia-cusparselt-cu13"
+version = "0.8.0"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/56/79/12978b96bd44274fe38b5dde5cfb660b1d114f70a65ef962bcbbed99b549/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f1bb701d6b930d5a7cea44c19ceb973311500847f81b634d802b7b539dc55623", size = 287193691, upload-time = "2025-02-26T00:15:44.104Z" },
+ { url = "https://files.pythonhosted.org/packages/46/10/8dcd1175260706a2fc92a16a52e306b71d4c1ea0b0cc4a9484183399818a/nvidia_cusparselt_cu13-0.8.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:400c6ed1cf6780fc6efedd64ec9f1345871767e6a1a0a552a1ea0578117ea77c", size = 220791277, upload-time = "2025-08-13T19:22:40.982Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/53/43b0d71f4e702fa9733f8b4571fdca50a8813f1e450b656c239beff12315/nvidia_cusparselt_cu13-0.8.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:25e30a8a7323935d4ad0340b95a0b69926eee755767e8e0b1cf8dd85b197d3fd", size = 169884119, upload-time = "2025-08-13T19:23:41.967Z" },
]
[[package]]
name = "nvidia-ml-py"
-version = "13.580.82"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/dd/6c/4a533f2c0185027c465adb6063086bc3728301e95f483665bfa9ebafb2d3/nvidia_ml_py-13.580.82.tar.gz", hash = "sha256:0c028805dc53a0e2a6985ea801888197765ac2ef8f1c9e29a7bf0d3616a5efc7", size = 47999, upload-time = "2025-09-11T16:44:56.267Z" }
+version = "13.595.45"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/ce/49/c29f6e30d8662d2e94fef17739ea7309cc76aba269922ae999e4cc07f268/nvidia_ml_py-13.595.45.tar.gz", hash = "sha256:c9f34897fe0441ff35bc8f35baf80f830a20b0f4e6ce71e0a325bc0e66acf079", size = 50780, upload-time = "2026-03-19T16:59:44.956Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/7f/96/d6d25a4c307d6645f4a9b91d620c0151c544ad38b5e371313a87d2761004/nvidia_ml_py-13.580.82-py3-none-any.whl", hash = "sha256:4361db337b0c551e2d101936dae2e9a60f957af26818e8c0c3a1f32b8db8d0a7", size = 49008, upload-time = "2025-09-11T16:44:54.915Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/24/fc256107d23597fa33d319505ce77160fa1a2349c096d01901ffc7cb7fc4/nvidia_ml_py-13.595.45-py3-none-any.whl", hash = "sha256:b65a7977f503d56154b14d683710125ef93594adb63fbf7e559336e3318f1376", size = 51776, upload-time = "2026-03-19T16:59:43.603Z" },
]
[[package]]
-name = "nvidia-nccl-cu12"
-version = "2.27.5"
-source = { registry = "https://pypi.org/simple" }
+name = "nvidia-nccl-cu13"
+version = "2.28.9"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/6e/89/f7a07dc961b60645dbbf42e80f2bc85ade7feb9a491b11a1e973aa00071f/nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ad730cf15cb5d25fe849c6e6ca9eb5b76db16a80f13f425ac68d8e2e55624457", size = 322348229, upload-time = "2025-06-26T04:11:28.385Z" },
+ { url = "https://files.pythonhosted.org/packages/39/55/1920646a2e43ffd4fc958536b276197ed740e9e0c54105b4bb3521591fc7/nvidia_nccl_cu13-2.28.9-py3-none-manylinux_2_18_aarch64.whl", hash = "sha256:01c873ba1626b54caa12272ed228dc5b2781545e0ae8ba3f432a8ef1c6d78643", size = 196561677, upload-time = "2025-11-18T05:49:03.45Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/b4/878fefaad5b2bcc6fcf8d474a25e3e3774bc5133e4b58adff4d0bca238bc/nvidia_nccl_cu13-2.28.9-py3-none-manylinux_2_18_x86_64.whl", hash = "sha256:e4553a30f34195f3fa1da02a6da3d6337d28f2003943aa0a3d247bbc25fefc42", size = 196493177, upload-time = "2025-11-18T05:49:17.677Z" },
]
[[package]]
-name = "nvidia-nvjitlink-cu12"
-version = "12.8.93"
-source = { registry = "https://pypi.org/simple" }
+name = "nvidia-nvjitlink"
+version = "13.0.88"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/f6/74/86a07f1d0f42998ca31312f998bd3b9a7eff7f52378f4f270c8679c77fb9/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:81ff63371a7ebd6e6451970684f916be2eab07321b73c9d244dc2b4da7f73b88", size = 39254836, upload-time = "2025-03-07T01:49:55.661Z" },
+ { url = "https://files.pythonhosted.org/packages/56/7a/123e033aaff487c77107195fa5a2b8686795ca537935a24efae476c41f05/nvidia_nvjitlink-13.0.88-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:13a74f429e23b921c1109976abefacc69835f2f433ebd323d3946e11d804e47b", size = 40713933, upload-time = "2025-09-04T08:35:43.553Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/2c/93c5250e64df4f894f1cbb397c6fd71f79813f9fd79d7cd61de3f97b3c2d/nvidia_nvjitlink-13.0.88-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e931536ccc7d467a98ba1d8b89ff7fa7f1fa3b13f2b0069118cd7f47bff07d0c", size = 38768748, upload-time = "2025-09-04T08:35:20.008Z" },
]
[[package]]
-name = "nvidia-nvshmem-cu12"
-version = "3.3.20"
-source = { registry = "https://pypi.org/simple" }
+name = "nvidia-nvshmem-cu13"
+version = "3.4.5"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/3b/6c/99acb2f9eb85c29fc6f3a7ac4dccfd992e22666dd08a642b303311326a97/nvidia_nvshmem_cu12-3.3.20-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d00f26d3f9b2e3c3065be895e3059d6479ea5c638a3f38c9fec49b1b9dd7c1e5", size = 124657145, upload-time = "2025-08-04T20:25:19.995Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/0f/05cc9c720236dcd2db9c1ab97fff629e96821be2e63103569da0c9b72f19/nvidia_nvshmem_cu13-3.4.5-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6dc2a197f38e5d0376ad52cd1a2a3617d3cdc150fd5966f4aee9bcebb1d68fe9", size = 60215947, upload-time = "2025-09-06T00:32:20.022Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/35/a9bf80a609e74e3b000fef598933235c908fcefcef9026042b8e6dfde2a9/nvidia_nvshmem_cu13-3.4.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:290f0a2ee94c9f3687a02502f3b9299a9f9fe826e6d0287ee18482e78d495b80", size = 60412546, upload-time = "2025-09-06T00:32:41.564Z" },
]
[[package]]
-name = "nvidia-nvtx-cu12"
-version = "12.8.90"
-source = { registry = "https://pypi.org/simple" }
+name = "nvidia-nvtx"
+version = "13.0.85"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/a2/eb/86626c1bbc2edb86323022371c39aa48df6fd8b0a1647bc274577f72e90b/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b17e2001cc0d751a5bc2c6ec6d26ad95913324a4adb86788c944f8ce9ba441f", size = 89954, upload-time = "2025-03-07T01:42:44.131Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/f3/d86c845465a2723ad7e1e5c36dcd75ddb82898b3f53be47ebd429fb2fa5d/nvidia_nvtx-13.0.85-py3-none-manylinux1_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4936d1d6780fbe68db454f5e72a42ff64d1fd6397df9f363ae786930fd5c1cd4", size = 148047, upload-time = "2025-09-04T08:29:01.761Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/64/3708a90d1ebe202ffdeb7185f878a3c84d15c2b2c31858da2ce0583e2def/nvidia_nvtx-13.0.85-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cb7780edb6b14107373c835bf8b72e7a178bac7367e23da7acb108f973f157a6", size = 148878, upload-time = "2025-09-04T08:28:53.627Z" },
]
[[package]]
name = "opencv-python-headless"
version = "4.11.0.86"
-source = { registry = "https://pypi.org/simple" }
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
dependencies = [
{ name = "numpy" },
]
@@ -960,7 +1062,7 @@ wheels = [
[[package]]
name = "opt-einsum"
version = "3.4.0"
-source = { registry = "https://pypi.org/simple" }
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/8c/b9/2ac072041e899a52f20cf9510850ff58295003aa75525e58343591b0cbfb/opt_einsum-3.4.0.tar.gz", hash = "sha256:96ca72f1b886d148241348783498194c577fa30a8faac108586b14f1ba4473ac", size = 63004, upload-time = "2024-09-26T14:33:24.483Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/23/cd/066e86230ae37ed0be70aae89aabf03ca8d9f39c8aea0dec8029455b5540/opt_einsum-3.4.0-py3-none-any.whl", hash = "sha256:69bb92469f86a1565195ece4ac0323943e83477171b91d24c35afe028a90d7cd", size = 71932, upload-time = "2024-09-26T14:33:23.039Z" },
@@ -969,7 +1071,7 @@ wheels = [
[[package]]
name = "optax"
version = "0.2.4"
-source = { registry = "https://pypi.org/simple" }
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
dependencies = [
{ name = "absl-py" },
{ name = "chex" },
@@ -985,22 +1087,22 @@ wheels = [
[[package]]
name = "packaging"
-version = "25.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" }
+version = "26.1"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/df/de/0d2b39fb4af88a0258f3bac87dfcbb48e73fbdea4a2ed0e2213f9a4c2f9a/packaging-26.1.tar.gz", hash = "sha256:f042152b681c4bfac5cae2742a55e103d27ab2ec0f3d88037136b6bfe7c9c5de", size = 215519, upload-time = "2026-04-14T21:12:49.362Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/c2/920ef838e2f0028c8262f16101ec09ebd5969864e5a64c4c05fad0617c56/packaging-26.1-py3-none-any.whl", hash = "sha256:5d9c0669c6285e491e0ced2eee587eaf67b670d94a19e94e3984a481aba6802f", size = 95831, upload-time = "2026-04-14T21:12:47.56Z" },
]
[[package]]
name = "pandas"
version = "2.3.3"
-source = { registry = "https://pypi.org/simple" }
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
resolution-markers = [
- "python_full_version >= '3.14' and sys_platform == 'darwin'",
- "python_full_version >= '3.14' and platform_machine == 'aarch64' and sys_platform == 'linux'",
"python_full_version >= '3.14' and sys_platform == 'win32'",
"python_full_version >= '3.14' and sys_platform == 'emscripten'",
+ "python_full_version >= '3.14' and sys_platform == 'darwin'",
+ "python_full_version >= '3.14' and platform_machine == 'aarch64' and sys_platform == 'linux'",
"(python_full_version >= '3.14' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.14' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'linux' and sys_platform != 'win32')",
]
dependencies = [
@@ -1055,18 +1157,18 @@ wheels = [
[[package]]
name = "pandas"
-version = "3.0.0"
-source = { registry = "https://pypi.org/simple" }
+version = "3.0.2"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
resolution-markers = [
- "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'darwin'",
- "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine == 'aarch64' and sys_platform == 'linux'",
"python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'win32'",
"python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'",
+ "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'darwin'",
+ "python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine == 'aarch64' and sys_platform == 'linux'",
"(python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'linux' and sys_platform != 'win32')",
- "python_full_version < '3.12' and sys_platform == 'darwin'",
- "python_full_version < '3.12' and platform_machine == 'aarch64' and sys_platform == 'linux'",
"python_full_version < '3.12' and sys_platform == 'win32'",
"python_full_version < '3.12' and sys_platform == 'emscripten'",
+ "python_full_version < '3.12' and sys_platform == 'darwin'",
+ "python_full_version < '3.12' and platform_machine == 'aarch64' and sys_platform == 'linux'",
"(python_full_version < '3.12' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.12' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'linux' and sys_platform != 'win32')",
]
dependencies = [
@@ -1074,148 +1176,163 @@ dependencies = [
{ name = "python-dateutil", marker = "python_full_version < '3.14'" },
{ name = "tzdata", marker = "(python_full_version < '3.14' and sys_platform == 'emscripten') or (python_full_version < '3.14' and sys_platform == 'win32')" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/de/da/b1dc0481ab8d55d0f46e343cfe67d4551a0e14fcee52bd38ca1bd73258d8/pandas-3.0.0.tar.gz", hash = "sha256:0facf7e87d38f721f0af46fe70d97373a37701b1c09f7ed7aeeb292ade5c050f", size = 4633005, upload-time = "2026-01-21T15:52:04.726Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/46/1e/b184654a856e75e975a6ee95d6577b51c271cd92cb2b020c9378f53e0032/pandas-3.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d64ce01eb9cdca96a15266aa679ae50212ec52757c79204dbc7701a222401850", size = 10313247, upload-time = "2026-01-21T15:50:15.775Z" },
- { url = "https://files.pythonhosted.org/packages/dd/5e/e04a547ad0f0183bf151fd7c7a477468e3b85ff2ad231c566389e6cc9587/pandas-3.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:613e13426069793aa1ec53bdcc3b86e8d32071daea138bbcf4fa959c9cdaa2e2", size = 9913131, upload-time = "2026-01-21T15:50:18.611Z" },
- { url = "https://files.pythonhosted.org/packages/a2/93/bb77bfa9fc2aba9f7204db807d5d3fb69832ed2854c60ba91b4c65ba9219/pandas-3.0.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0192fee1f1a8e743b464a6607858ee4b071deb0b118eb143d71c2a1d170996d5", size = 10741925, upload-time = "2026-01-21T15:50:21.058Z" },
- { url = "https://files.pythonhosted.org/packages/62/fb/89319812eb1d714bfc04b7f177895caeba8ab4a37ef6712db75ed786e2e0/pandas-3.0.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f0b853319dec8d5e0c8b875374c078ef17f2269986a78168d9bd57e49bf650ae", size = 11245979, upload-time = "2026-01-21T15:50:23.413Z" },
- { url = "https://files.pythonhosted.org/packages/a9/63/684120486f541fc88da3862ed31165b3b3e12b6a1c7b93be4597bc84e26c/pandas-3.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:707a9a877a876c326ae2cb640fbdc4ef63b0a7b9e2ef55c6df9942dcee8e2af9", size = 11756337, upload-time = "2026-01-21T15:50:25.932Z" },
- { url = "https://files.pythonhosted.org/packages/39/92/7eb0ad232312b59aec61550c3c81ad0743898d10af5df7f80bc5e5065416/pandas-3.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:afd0aa3d0b5cda6e0b8ffc10dbcca3b09ef3cbcd3fe2b27364f85fdc04e1989d", size = 12325517, upload-time = "2026-01-21T15:50:27.952Z" },
- { url = "https://files.pythonhosted.org/packages/51/27/bf9436dd0a4fc3130acec0828951c7ef96a0631969613a9a35744baf27f6/pandas-3.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:113b4cca2614ff7e5b9fee9b6f066618fe73c5a83e99d721ffc41217b2bf57dd", size = 9881576, upload-time = "2026-01-21T15:50:30.149Z" },
- { url = "https://files.pythonhosted.org/packages/e7/2b/c618b871fce0159fd107516336e82891b404e3f340821853c2fc28c7830f/pandas-3.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c14837eba8e99a8da1527c0280bba29b0eb842f64aa94982c5e21227966e164b", size = 9140807, upload-time = "2026-01-21T15:50:32.308Z" },
- { url = "https://files.pythonhosted.org/packages/0b/38/db33686f4b5fa64d7af40d96361f6a4615b8c6c8f1b3d334eee46ae6160e/pandas-3.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9803b31f5039b3c3b10cc858c5e40054adb4b29b4d81cb2fd789f4121c8efbcd", size = 10334013, upload-time = "2026-01-21T15:50:34.771Z" },
- { url = "https://files.pythonhosted.org/packages/a5/7b/9254310594e9774906bacdd4e732415e1f86ab7dbb4b377ef9ede58cd8ec/pandas-3.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:14c2a4099cd38a1d18ff108168ea417909b2dea3bd1ebff2ccf28ddb6a74d740", size = 9874154, upload-time = "2026-01-21T15:50:36.67Z" },
- { url = "https://files.pythonhosted.org/packages/63/d4/726c5a67a13bc66643e66d2e9ff115cead482a44fc56991d0c4014f15aaf/pandas-3.0.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d257699b9a9960e6125686098d5714ac59d05222bef7a5e6af7a7fd87c650801", size = 10384433, upload-time = "2026-01-21T15:50:39.132Z" },
- { url = "https://files.pythonhosted.org/packages/bf/2e/9211f09bedb04f9832122942de8b051804b31a39cfbad199a819bb88d9f3/pandas-3.0.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:69780c98f286076dcafca38d8b8eee1676adf220199c0a39f0ecbf976b68151a", size = 10864519, upload-time = "2026-01-21T15:50:41.043Z" },
- { url = "https://files.pythonhosted.org/packages/00/8d/50858522cdc46ac88b9afdc3015e298959a70a08cd21e008a44e9520180c/pandas-3.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4a66384f017240f3858a4c8a7cf21b0591c3ac885cddb7758a589f0f71e87ebb", size = 11394124, upload-time = "2026-01-21T15:50:43.377Z" },
- { url = "https://files.pythonhosted.org/packages/86/3f/83b2577db02503cd93d8e95b0f794ad9d4be0ba7cb6c8bcdcac964a34a42/pandas-3.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:be8c515c9bc33989d97b89db66ea0cececb0f6e3c2a87fcc8b69443a6923e95f", size = 11920444, upload-time = "2026-01-21T15:50:45.932Z" },
- { url = "https://files.pythonhosted.org/packages/64/2d/4f8a2f192ed12c90a0aab47f5557ece0e56b0370c49de9454a09de7381b2/pandas-3.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:a453aad8c4f4e9f166436994a33884442ea62aa8b27d007311e87521b97246e1", size = 9730970, upload-time = "2026-01-21T15:50:47.962Z" },
- { url = "https://files.pythonhosted.org/packages/d4/64/ff571be435cf1e643ca98d0945d76732c0b4e9c37191a89c8550b105eed1/pandas-3.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:da768007b5a33057f6d9053563d6b74dd6d029c337d93c6d0d22a763a5c2ecc0", size = 9041950, upload-time = "2026-01-21T15:50:50.422Z" },
- { url = "https://files.pythonhosted.org/packages/6f/fa/7f0ac4ca8877c57537aaff2a842f8760e630d8e824b730eb2e859ffe96ca/pandas-3.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b78d646249b9a2bc191040988c7bb524c92fa8534fb0898a0741d7e6f2ffafa6", size = 10307129, upload-time = "2026-01-21T15:50:52.877Z" },
- { url = "https://files.pythonhosted.org/packages/6f/11/28a221815dcea4c0c9414dfc845e34a84a6a7dabc6da3194498ed5ba4361/pandas-3.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bc9cba7b355cb4162442a88ce495e01cb605f17ac1e27d6596ac963504e0305f", size = 9850201, upload-time = "2026-01-21T15:50:54.807Z" },
- { url = "https://files.pythonhosted.org/packages/ba/da/53bbc8c5363b7e5bd10f9ae59ab250fc7a382ea6ba08e4d06d8694370354/pandas-3.0.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c9a1a149aed3b6c9bf246033ff91e1b02d529546c5d6fb6b74a28fea0cf4c70", size = 10354031, upload-time = "2026-01-21T15:50:57.463Z" },
- { url = "https://files.pythonhosted.org/packages/f7/a3/51e02ebc2a14974170d51e2410dfdab58870ea9bcd37cda15bd553d24dc4/pandas-3.0.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:95683af6175d884ee89471842acfca29172a85031fccdabc35e50c0984470a0e", size = 10861165, upload-time = "2026-01-21T15:50:59.32Z" },
- { url = "https://files.pythonhosted.org/packages/a5/fe/05a51e3cac11d161472b8297bd41723ea98013384dd6d76d115ce3482f9b/pandas-3.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1fbbb5a7288719e36b76b4f18d46ede46e7f916b6c8d9915b756b0a6c3f792b3", size = 11359359, upload-time = "2026-01-21T15:51:02.014Z" },
- { url = "https://files.pythonhosted.org/packages/ee/56/ba620583225f9b85a4d3e69c01df3e3870659cc525f67929b60e9f21dcd1/pandas-3.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8e8b9808590fa364416b49b2a35c1f4cf2785a6c156935879e57f826df22038e", size = 11912907, upload-time = "2026-01-21T15:51:05.175Z" },
- { url = "https://files.pythonhosted.org/packages/c9/8c/c6638d9f67e45e07656b3826405c5cc5f57f6fd07c8b2572ade328c86e22/pandas-3.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:98212a38a709feb90ae658cb6227ea3657c22ba8157d4b8f913cd4c950de5e7e", size = 9732138, upload-time = "2026-01-21T15:51:07.569Z" },
- { url = "https://files.pythonhosted.org/packages/7b/bf/bd1335c3bf1770b6d8fed2799993b11c4971af93bb1b729b9ebbc02ca2ec/pandas-3.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:177d9df10b3f43b70307a149d7ec49a1229a653f907aa60a48f1877d0e6be3be", size = 9033568, upload-time = "2026-01-21T15:51:09.484Z" },
- { url = "https://files.pythonhosted.org/packages/8e/c6/f5e2171914d5e29b9171d495344097d54e3ffe41d2d85d8115baba4dc483/pandas-3.0.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2713810ad3806767b89ad3b7b69ba153e1c6ff6d9c20f9c2140379b2a98b6c98", size = 10741936, upload-time = "2026-01-21T15:51:11.693Z" },
- { url = "https://files.pythonhosted.org/packages/51/88/9a0164f99510a1acb9f548691f022c756c2314aad0d8330a24616c14c462/pandas-3.0.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:15d59f885ee5011daf8335dff47dcb8a912a27b4ad7826dc6cbe809fd145d327", size = 10393884, upload-time = "2026-01-21T15:51:14.197Z" },
- { url = "https://files.pythonhosted.org/packages/e0/53/b34d78084d88d8ae2b848591229da8826d1e65aacf00b3abe34023467648/pandas-3.0.0-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:24e6547fb64d2c92665dd2adbfa4e85fa4fd70a9c070e7cfb03b629a0bbab5eb", size = 10310740, upload-time = "2026-01-21T15:51:16.093Z" },
- { url = "https://files.pythonhosted.org/packages/5b/d3/bee792e7c3d6930b74468d990604325701412e55d7aaf47460a22311d1a5/pandas-3.0.0-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:48ee04b90e2505c693d3f8e8f524dab8cb8aaf7ddcab52c92afa535e717c4812", size = 10700014, upload-time = "2026-01-21T15:51:18.818Z" },
- { url = "https://files.pythonhosted.org/packages/55/db/2570bc40fb13aaed1cbc3fbd725c3a60ee162477982123c3adc8971e7ac1/pandas-3.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:66f72fb172959af42a459e27a8d8d2c7e311ff4c1f7db6deb3b643dbc382ae08", size = 11323737, upload-time = "2026-01-21T15:51:20.784Z" },
- { url = "https://files.pythonhosted.org/packages/bc/2e/297ac7f21c8181b62a4cccebad0a70caf679adf3ae5e83cb676194c8acc3/pandas-3.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4a4a400ca18230976724a5066f20878af785f36c6756e498e94c2a5e5d57779c", size = 11771558, upload-time = "2026-01-21T15:51:22.977Z" },
- { url = "https://files.pythonhosted.org/packages/0a/46/e1c6876d71c14332be70239acce9ad435975a80541086e5ffba2f249bcf6/pandas-3.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:940eebffe55528074341a5a36515f3e4c5e25e958ebbc764c9502cfc35ba3faa", size = 10473771, upload-time = "2026-01-21T15:51:25.285Z" },
- { url = "https://files.pythonhosted.org/packages/c0/db/0270ad9d13c344b7a36fa77f5f8344a46501abf413803e885d22864d10bf/pandas-3.0.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:597c08fb9fef0edf1e4fa2f9828dd27f3d78f9b8c9b4a748d435ffc55732310b", size = 10312075, upload-time = "2026-01-21T15:51:28.5Z" },
- { url = "https://files.pythonhosted.org/packages/09/9f/c176f5e9717f7c91becfe0f55a52ae445d3f7326b4a2cf355978c51b7913/pandas-3.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:447b2d68ac5edcbf94655fe909113a6dba6ef09ad7f9f60c80477825b6c489fe", size = 9900213, upload-time = "2026-01-21T15:51:30.955Z" },
- { url = "https://files.pythonhosted.org/packages/d9/e7/63ad4cc10b257b143e0a5ebb04304ad806b4e1a61c5da25f55896d2ca0f4/pandas-3.0.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:debb95c77ff3ed3ba0d9aa20c3a2f19165cc7956362f9873fce1ba0a53819d70", size = 10428768, upload-time = "2026-01-21T15:51:33.018Z" },
- { url = "https://files.pythonhosted.org/packages/9e/0e/4e4c2d8210f20149fd2248ef3fff26623604922bd564d915f935a06dd63d/pandas-3.0.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fedabf175e7cd82b69b74c30adbaa616de301291a5231138d7242596fc296a8d", size = 10882954, upload-time = "2026-01-21T15:51:35.287Z" },
- { url = "https://files.pythonhosted.org/packages/c6/60/c9de8ac906ba1f4d2250f8a951abe5135b404227a55858a75ad26f84db47/pandas-3.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:412d1a89aab46889f3033a386912efcdfa0f1131c5705ff5b668dda88305e986", size = 11430293, upload-time = "2026-01-21T15:51:37.57Z" },
- { url = "https://files.pythonhosted.org/packages/a1/69/806e6637c70920e5787a6d6896fd707f8134c2c55cd761e7249a97b7dc5a/pandas-3.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e979d22316f9350c516479dd3a92252be2937a9531ed3a26ec324198a99cdd49", size = 11952452, upload-time = "2026-01-21T15:51:39.618Z" },
- { url = "https://files.pythonhosted.org/packages/cb/de/918621e46af55164c400ab0ef389c9d969ab85a43d59ad1207d4ddbe30a5/pandas-3.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:083b11415b9970b6e7888800c43c82e81a06cd6b06755d84804444f0007d6bb7", size = 9851081, upload-time = "2026-01-21T15:51:41.758Z" },
- { url = "https://files.pythonhosted.org/packages/91/a1/3562a18dd0bd8c73344bfa26ff90c53c72f827df119d6d6b1dacc84d13e3/pandas-3.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:5db1e62cb99e739fa78a28047e861b256d17f88463c76b8dafc7c1338086dca8", size = 9174610, upload-time = "2026-01-21T15:51:44.312Z" },
- { url = "https://files.pythonhosted.org/packages/ce/26/430d91257eaf366f1737d7a1c158677caaf6267f338ec74e3a1ec444111c/pandas-3.0.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:697b8f7d346c68274b1b93a170a70974cdc7d7354429894d5927c1effdcccd73", size = 10761999, upload-time = "2026-01-21T15:51:46.899Z" },
- { url = "https://files.pythonhosted.org/packages/ec/1a/954eb47736c2b7f7fe6a9d56b0cb6987773c00faa3c6451a43db4beb3254/pandas-3.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8cb3120f0d9467ed95e77f67a75e030b67545bcfa08964e349252d674171def2", size = 10410279, upload-time = "2026-01-21T15:51:48.89Z" },
- { url = "https://files.pythonhosted.org/packages/20/fc/b96f3a5a28b250cd1b366eb0108df2501c0f38314a00847242abab71bb3a/pandas-3.0.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:33fd3e6baa72899746b820c31e4b9688c8e1b7864d7aec2de7ab5035c285277a", size = 10330198, upload-time = "2026-01-21T15:51:51.015Z" },
- { url = "https://files.pythonhosted.org/packages/90/b3/d0e2952f103b4fbef1ef22d0c2e314e74fc9064b51cee30890b5e3286ee6/pandas-3.0.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8942e333dc67ceda1095227ad0febb05a3b36535e520154085db632c40ad084", size = 10728513, upload-time = "2026-01-21T15:51:53.387Z" },
- { url = "https://files.pythonhosted.org/packages/76/81/832894f286df828993dc5fd61c63b231b0fb73377e99f6c6c369174cf97e/pandas-3.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:783ac35c4d0fe0effdb0d67161859078618b1b6587a1af15928137525217a721", size = 11345550, upload-time = "2026-01-21T15:51:55.329Z" },
- { url = "https://files.pythonhosted.org/packages/34/a0/ed160a00fb4f37d806406bc0a79a8b62fe67f29d00950f8d16203ff3409b/pandas-3.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:125eb901e233f155b268bbef9abd9afb5819db74f0e677e89a61b246228c71ac", size = 11799386, upload-time = "2026-01-21T15:51:57.457Z" },
- { url = "https://files.pythonhosted.org/packages/36/c8/2ac00d7255252c5e3cf61b35ca92ca25704b0188f7454ca4aec08a33cece/pandas-3.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b86d113b6c109df3ce0ad5abbc259fe86a1bd4adfd4a31a89da42f84f65509bb", size = 10873041, upload-time = "2026-01-21T15:52:00.034Z" },
- { url = "https://files.pythonhosted.org/packages/e6/3f/a80ac00acbc6b35166b42850e98a4f466e2c0d9c64054161ba9620f95680/pandas-3.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:1c39eab3ad38f2d7a249095f0a3d8f8c22cc0f847e98ccf5bbe732b272e2d9fa", size = 9441003, upload-time = "2026-01-21T15:52:02.281Z" },
+sdist = { url = "https://files.pythonhosted.org/packages/da/99/b342345300f13440fe9fe385c3c481e2d9a595ee3bab4d3219247ac94e9a/pandas-3.0.2.tar.gz", hash = "sha256:f4753e73e34c8d83221ba58f232433fca2748be8b18dbca02d242ed153945043", size = 4645855, upload-time = "2026-03-31T06:48:30.816Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/97/35/6411db530c618e0e0005187e35aa02ce60ae4c4c4d206964a2f978217c27/pandas-3.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a727a73cbdba2f7458dc82449e2315899d5140b449015d822f515749a46cbbe0", size = 10326926, upload-time = "2026-03-31T06:46:08.29Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/d3/b7da1d5d7dbdc5ef52ed7debd2b484313b832982266905315dad5a0bf0b1/pandas-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dbbd4aa20ca51e63b53bbde6a0fa4254b1aaabb74d2f542df7a7959feb1d760c", size = 9926987, upload-time = "2026-03-31T06:46:11.724Z" },
+ { url = "https://files.pythonhosted.org/packages/52/77/9b1c2d6070b5dbe239a7bc889e21bfa58720793fb902d1e070695d87c6d0/pandas-3.0.2-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:339dda302bd8369dedeae979cb750e484d549b563c3f54f3922cb8ff4978c5eb", size = 10757067, upload-time = "2026-03-31T06:46:14.903Z" },
+ { url = "https://files.pythonhosted.org/packages/20/17/ec40d981705654853726e7ac9aea9ddbb4a5d9cf54d8472222f4f3de06c2/pandas-3.0.2-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:61c2fd96d72b983a9891b2598f286befd4ad262161a609c92dc1652544b46b76", size = 11258787, upload-time = "2026-03-31T06:46:17.683Z" },
+ { url = "https://files.pythonhosted.org/packages/90/e3/3f1126d43d3702ca8773871a81c9f15122a1f412342cc56284ffda5b1f70/pandas-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c934008c733b8bbea273ea308b73b3156f0181e5b72960790b09c18a2794fe1e", size = 11771616, upload-time = "2026-03-31T06:46:20.532Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/cf/0f4e268e1f5062e44a6bda9f925806721cd4c95c2b808a4c82ebe914f96b/pandas-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:60a80bb4feacbef5e1447a3f82c33209c8b7e07f28d805cfd1fb951e5cb443aa", size = 12337623, upload-time = "2026-03-31T06:46:23.754Z" },
+ { url = "https://files.pythonhosted.org/packages/44/a0/97a6339859d4acb2536efb24feb6708e82f7d33b2ed7e036f2983fcced82/pandas-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:ed72cb3f45190874eb579c64fa92d9df74e98fd63e2be7f62bce5ace0ade61df", size = 9897372, upload-time = "2026-03-31T06:46:26.703Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/eb/781516b808a99ddf288143cec46b342b3016c3414d137da1fdc3290d8860/pandas-3.0.2-cp311-cp311-win_arm64.whl", hash = "sha256:f12b1a9e332c01e09510586f8ca9b108fd631fd656af82e452d7315ef6df5f9f", size = 9154922, upload-time = "2026-03-31T06:46:30.284Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/b0/c20bd4d6d3f736e6bd6b55794e9cd0a617b858eaad27c8f410ea05d953b7/pandas-3.0.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:232a70ebb568c0c4d2db4584f338c1577d81e3af63292208d615907b698a0f18", size = 10347921, upload-time = "2026-03-31T06:46:33.36Z" },
+ { url = "https://files.pythonhosted.org/packages/35/d0/4831af68ce30cc2d03c697bea8450e3225a835ef497d0d70f31b8cdde965/pandas-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:970762605cff1ca0d3f71ed4f3a769ea8f85fc8e6348f6e110b8fea7e6eb5a14", size = 9888127, upload-time = "2026-03-31T06:46:36.253Z" },
+ { url = "https://files.pythonhosted.org/packages/61/a9/16ea9346e1fc4a96e2896242d9bc674764fb9049b0044c0132502f7a771e/pandas-3.0.2-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aff4e6f4d722e0652707d7bcb190c445fe58428500c6d16005b02401764b1b3d", size = 10399577, upload-time = "2026-03-31T06:46:39.224Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/a8/3a61a721472959ab0ce865ef05d10b0d6bfe27ce8801c99f33d4fa996e65/pandas-3.0.2-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ef8b27695c3d3dc78403c9a7d5e59a62d5464a7e1123b4e0042763f7104dc74f", size = 10880030, upload-time = "2026-03-31T06:46:42.412Z" },
+ { url = "https://files.pythonhosted.org/packages/da/65/7225c0ea4d6ce9cb2160a7fb7f39804871049f016e74782e5dade4d14109/pandas-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f8d68083e49e16b84734eb1a4dcae4259a75c90fb6e2251ab9a00b61120c06ab", size = 11409468, upload-time = "2026-03-31T06:46:45.2Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/5b/46e7c76032639f2132359b5cf4c785dd8cf9aea5ea64699eac752f02b9db/pandas-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:32cc41f310ebd4a296d93515fcac312216adfedb1894e879303987b8f1e2b97d", size = 11936381, upload-time = "2026-03-31T06:46:48.293Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/8b/721a9cff6fa6a91b162eb51019c6243b82b3226c71bb6c8ef4a9bd65cbc6/pandas-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:a4785e1d6547d8427c5208b748ae2efb64659a21bd82bf440d4262d02bfa02a4", size = 9744993, upload-time = "2026-03-31T06:46:51.488Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/18/7f0bd34ae27b28159aa80f2a6799f47fda34f7fb938a76e20c7b7fe3b200/pandas-3.0.2-cp312-cp312-win_arm64.whl", hash = "sha256:08504503f7101300107ecdc8df73658e4347586db5cfdadabc1592e9d7e7a0fd", size = 9056118, upload-time = "2026-03-31T06:46:54.548Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/ca/3e639a1ea6fcd0617ca4e8ca45f62a74de33a56ae6cd552735470b22c8d3/pandas-3.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b5918ba197c951dec132b0c5929a00c0bf05d5942f590d3c10a807f6e15a57d3", size = 10321105, upload-time = "2026-03-31T06:46:57.327Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/77/dbc82ff2fb0e63c6564356682bf201edff0ba16c98630d21a1fb312a8182/pandas-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d606a041c89c0a474a4702d532ab7e73a14fe35c8d427b972a625c8e46373668", size = 9864088, upload-time = "2026-03-31T06:46:59.935Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/2b/341f1b04bbca2e17e13cd3f08c215b70ef2c60c5356ef1e8c6857449edc7/pandas-3.0.2-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:710246ba0616e86891b58ab95f2495143bb2bc83ab6b06747c74216f583a6ac9", size = 10369066, upload-time = "2026-03-31T06:47:02.792Z" },
+ { url = "https://files.pythonhosted.org/packages/12/c5/cbb1ffefb20a93d3f0e1fdcda699fb84976210d411b008f97f48bf6ce27e/pandas-3.0.2-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5d3cfe227c725b1f3dff4278b43d8c784656a42a9325b63af6b1492a8232209e", size = 10876780, upload-time = "2026-03-31T06:47:06.205Z" },
+ { url = "https://files.pythonhosted.org/packages/98/fe/2249ae5e0a69bd0ddf17353d0a5d26611d70970111f5b3600cdc8be883e7/pandas-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c3b723df9087a9a9a840e263ebd9f88b64a12075d1bf2ea401a5a42f254f084d", size = 11375181, upload-time = "2026-03-31T06:47:09.383Z" },
+ { url = "https://files.pythonhosted.org/packages/de/64/77a38b09e70b6464883b8d7584ab543e748e42c1b5d337a2ee088e0df741/pandas-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a3096110bf9eac0070b7208465f2740e2d8a670d5cb6530b5bb884eca495fd39", size = 11928899, upload-time = "2026-03-31T06:47:12.686Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/52/42855bf626868413f761addd574acc6195880ae247a5346477a4361c3acb/pandas-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:07a10f5c36512eead51bc578eb3354ad17578b22c013d89a796ab5eee90cd991", size = 9746574, upload-time = "2026-03-31T06:47:15.64Z" },
+ { url = "https://files.pythonhosted.org/packages/88/39/21304ae06a25e8bf9fc820d69b29b2c495b2ae580d1e143146c309941760/pandas-3.0.2-cp313-cp313-win_arm64.whl", hash = "sha256:5fdbfa05931071aba28b408e59226186b01eb5e92bea2ab78b65863ca3228d84", size = 9047156, upload-time = "2026-03-31T06:47:18.595Z" },
+ { url = "https://files.pythonhosted.org/packages/72/20/7defa8b27d4f330a903bb68eea33be07d839c5ea6bdda54174efcec0e1d2/pandas-3.0.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:dbc20dea3b9e27d0e66d74c42b2d0c1bed9c2ffe92adea33633e3bedeb5ac235", size = 10756238, upload-time = "2026-03-31T06:47:22.012Z" },
+ { url = "https://files.pythonhosted.org/packages/e9/95/49433c14862c636afc0e9b2db83ff16b3ad92959364e52b2955e44c8e94c/pandas-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b75c347eff42497452116ce05ef461822d97ce5b9ff8df6edacb8076092c855d", size = 10408520, upload-time = "2026-03-31T06:47:25.197Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/f8/462ad2b5881d6b8ec8e5f7ed2ea1893faa02290d13870a1600fe72ad8efc/pandas-3.0.2-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1478075142e83a5571782ad007fb201ed074bdeac7ebcc8890c71442e96adf7", size = 10324154, upload-time = "2026-03-31T06:47:28.097Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/65/d1e69b649cbcddda23ad6e4c40ef935340f6f652a006e5cbc3555ac8adb3/pandas-3.0.2-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5880314e69e763d4c8b27937090de570f1fb8d027059a7ada3f7f8e98bdcb677", size = 10714449, upload-time = "2026-03-31T06:47:30.85Z" },
+ { url = "https://files.pythonhosted.org/packages/47/a4/85b59bc65b8190ea3689882db6cdf32a5003c0ccd5a586c30fdcc3ffc4fc/pandas-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b5329e26898896f06035241a626d7c335daa479b9bbc82be7c2742d048e41172", size = 11338475, upload-time = "2026-03-31T06:47:34.026Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/c4/bc6966c6e38e5d9478b935272d124d80a589511ed1612a5d21d36f664c68/pandas-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:81526c4afd31971f8b62671442a4b2b51e0aa9acc3819c9f0f12a28b6fcf85f1", size = 11786568, upload-time = "2026-03-31T06:47:36.941Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/74/09298ca9740beed1d3504e073d67e128aa07e5ca5ca2824b0c674c0b8676/pandas-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:7cadd7e9a44ec13b621aec60f9150e744cfc7a3dd32924a7e2f45edff31823b0", size = 10488652, upload-time = "2026-03-31T06:47:40.612Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/40/c6ea527147c73b24fc15c891c3fcffe9c019793119c5742b8784a062c7db/pandas-3.0.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:db0dbfd2a6cdf3770aa60464d50333d8f3d9165b2f2671bcc299b72de5a6677b", size = 10326084, upload-time = "2026-03-31T06:47:43.834Z" },
+ { url = "https://files.pythonhosted.org/packages/95/25/bdb9326c3b5455f8d4d3549fce7abcf967259de146fe2cf7a82368141948/pandas-3.0.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0555c5882688a39317179ab4a0ed41d3ebc8812ab14c69364bbee8fb7a3f6288", size = 9914146, upload-time = "2026-03-31T06:47:46.67Z" },
+ { url = "https://files.pythonhosted.org/packages/8d/77/3a227ff3337aa376c60d288e1d61c5d097131d0ac71f954d90a8f369e422/pandas-3.0.2-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:01f31a546acd5574ef77fe199bc90b55527c225c20ccda6601cf6b0fd5ed597c", size = 10444081, upload-time = "2026-03-31T06:47:49.681Z" },
+ { url = "https://files.pythonhosted.org/packages/15/88/3cdd54fa279341afa10acf8d2b503556b1375245dccc9315659f795dd2e9/pandas-3.0.2-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:deeca1b5a931fdf0c2212c8a659ade6d3b1edc21f0914ce71ef24456ca7a6535", size = 10897535, upload-time = "2026-03-31T06:47:53.033Z" },
+ { url = "https://files.pythonhosted.org/packages/06/9d/98cc7a7624f7932e40f434299260e2917b090a579d75937cb8a57b9d2de3/pandas-3.0.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0f48afd9bb13300ffb5a3316973324c787054ba6665cda0da3fbd67f451995db", size = 11446992, upload-time = "2026-03-31T06:47:56.193Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/cd/19ff605cc3760e80602e6826ddef2824d8e7050ed80f2e11c4b079741dc3/pandas-3.0.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6c4d8458b97a35717b62469a4ea0e85abd5ed8687277f5ccfc67f8a5126f8c53", size = 11968257, upload-time = "2026-03-31T06:47:59.137Z" },
+ { url = "https://files.pythonhosted.org/packages/db/60/aba6a38de456e7341285102bede27514795c1eaa353bc0e7638b6b785356/pandas-3.0.2-cp314-cp314-win_amd64.whl", hash = "sha256:b35d14bb5d8285d9494fe93815a9e9307c0876e10f1e8e89ac5b88f728ec8dcf", size = 9865893, upload-time = "2026-03-31T06:48:02.038Z" },
+ { url = "https://files.pythonhosted.org/packages/08/71/e5ec979dd2e8a093dacb8864598c0ff59a0cee0bbcdc0bfec16a51684d4f/pandas-3.0.2-cp314-cp314-win_arm64.whl", hash = "sha256:63d141b56ef686f7f0d714cfb8de4e320475b86bf4b620aa0b7da89af8cbdbbb", size = 9188644, upload-time = "2026-03-31T06:48:05.045Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/6c/7b45d85db19cae1eb524f2418ceaa9d85965dcf7b764ed151386b7c540f0/pandas-3.0.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:140f0cffb1fa2524e874dde5b477d9defe10780d8e9e220d259b2c0874c89d9d", size = 10776246, upload-time = "2026-03-31T06:48:07.789Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/3e/7b00648b086c106e81766f25322b48aa8dfa95b55e621dbdf2fdd413a117/pandas-3.0.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ae37e833ff4fed0ba352f6bdd8b73ba3ab3256a85e54edfd1ab51ae40cca0af8", size = 10424801, upload-time = "2026-03-31T06:48:10.897Z" },
+ { url = "https://files.pythonhosted.org/packages/da/6e/558dd09a71b53b4008e7fc8a98ec6d447e9bfb63cdaeea10e5eb9b2dabe8/pandas-3.0.2-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4d888a5c678a419a5bb41a2a93818e8ed9fd3172246555c0b37b7cc27027effd", size = 10345643, upload-time = "2026-03-31T06:48:13.7Z" },
+ { url = "https://files.pythonhosted.org/packages/be/e3/921c93b4d9a280409451dc8d07b062b503bbec0531d2627e73a756e99a82/pandas-3.0.2-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b444dc64c079e84df91baa8bf613d58405645461cabca929d9178f2cd392398d", size = 10743641, upload-time = "2026-03-31T06:48:16.659Z" },
+ { url = "https://files.pythonhosted.org/packages/56/ca/fd17286f24fa3b4d067965d8d5d7e14fe557dd4f979a0b068ac0deaf8228/pandas-3.0.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4544c7a54920de8eeacaa1466a6b7268ecfbc9bc64ab4dbb89c6bbe94d5e0660", size = 11361993, upload-time = "2026-03-31T06:48:19.475Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/a5/2f6ed612056819de445a433ca1f2821ac3dab7f150d569a59e9cc105de1d/pandas-3.0.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:734be7551687c00fbd760dc0522ed974f82ad230d4a10f54bf51b80d44a08702", size = 11815274, upload-time = "2026-03-31T06:48:22.695Z" },
+ { url = "https://files.pythonhosted.org/packages/00/2f/b622683e99ec3ce00b0854bac9e80868592c5b051733f2cf3a868e5fea26/pandas-3.0.2-cp314-cp314t-win_amd64.whl", hash = "sha256:57a07209bebcbcf768d2d13c9b78b852f9a15978dac41b9e6421a81ad4cdd276", size = 10888530, upload-time = "2026-03-31T06:48:25.806Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/2b/f8434233fab2bd66a02ec014febe4e5adced20e2693e0e90a07d118ed30e/pandas-3.0.2-cp314-cp314t-win_arm64.whl", hash = "sha256:5371b72c2d4d415d08765f32d689217a43227484e81b2305b52076e328f6f482", size = 9455341, upload-time = "2026-03-31T06:48:28.418Z" },
+]
+
+[[package]]
+name = "pdoc"
+version = "16.0.0"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
+dependencies = [
+ { name = "jinja2" },
+ { name = "markdown2" },
+ { name = "markupsafe" },
+ { name = "pygments" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/ac/fe/ab3f34a5fb08c6b698439a2c2643caf8fef0d61a86dd3fdcd5501c670ab8/pdoc-16.0.0.tar.gz", hash = "sha256:fdadc40cc717ec53919e3cd720390d4e3bcd40405cb51c4918c119447f913514", size = 111890, upload-time = "2025-10-27T16:02:16.345Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/16/a1/56a17b7f9e18c2bb8df73f3833345d97083b344708b97bab148fdd7e0b82/pdoc-16.0.0-py3-none-any.whl", hash = "sha256:070b51de2743b9b1a4e0ab193a06c9e6c12cf4151cf9137656eebb16e8556628", size = 100014, upload-time = "2025-10-27T16:02:15.007Z" },
]
[[package]]
name = "pillow"
-version = "12.0.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/cace85a1b0c9775a9f8f5d5423c8261c858760e2466c79b2dd184638b056/pillow-12.0.0.tar.gz", hash = "sha256:87d4f8125c9988bfbed67af47dd7a953e2fc7b0cc1e7800ec6d2080d490bb353", size = 47008828, upload-time = "2025-10-15T18:24:14.008Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/0e/5a/a2f6773b64edb921a756eb0729068acad9fc5208a53f4a349396e9436721/pillow-12.0.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:0fd00cac9c03256c8b2ff58f162ebcd2587ad3e1f2e397eab718c47e24d231cc", size = 5289798, upload-time = "2025-10-15T18:21:47.763Z" },
- { url = "https://files.pythonhosted.org/packages/2e/05/069b1f8a2e4b5a37493da6c5868531c3f77b85e716ad7a590ef87d58730d/pillow-12.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3475b96f5908b3b16c47533daaa87380c491357d197564e0ba34ae75c0f3257", size = 4650589, upload-time = "2025-10-15T18:21:49.515Z" },
- { url = "https://files.pythonhosted.org/packages/61/e3/2c820d6e9a36432503ead175ae294f96861b07600a7156154a086ba7111a/pillow-12.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:110486b79f2d112cf6add83b28b627e369219388f64ef2f960fef9ebaf54c642", size = 6230472, upload-time = "2025-10-15T18:21:51.052Z" },
- { url = "https://files.pythonhosted.org/packages/4f/89/63427f51c64209c5e23d4d52071c8d0f21024d3a8a487737caaf614a5795/pillow-12.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5269cc1caeedb67e6f7269a42014f381f45e2e7cd42d834ede3c703a1d915fe3", size = 8033887, upload-time = "2025-10-15T18:21:52.604Z" },
- { url = "https://files.pythonhosted.org/packages/f6/1b/c9711318d4901093c15840f268ad649459cd81984c9ec9887756cca049a5/pillow-12.0.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa5129de4e174daccbc59d0a3b6d20eaf24417d59851c07ebb37aeb02947987c", size = 6343964, upload-time = "2025-10-15T18:21:54.619Z" },
- { url = "https://files.pythonhosted.org/packages/41/1e/db9470f2d030b4995083044cd8738cdd1bf773106819f6d8ba12597d5352/pillow-12.0.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bee2a6db3a7242ea309aa7ee8e2780726fed67ff4e5b40169f2c940e7eb09227", size = 7034756, upload-time = "2025-10-15T18:21:56.151Z" },
- { url = "https://files.pythonhosted.org/packages/cc/b0/6177a8bdd5ee4ed87cba2de5a3cc1db55ffbbec6176784ce5bb75aa96798/pillow-12.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:90387104ee8400a7b4598253b4c406f8958f59fcf983a6cea2b50d59f7d63d0b", size = 6458075, upload-time = "2025-10-15T18:21:57.759Z" },
- { url = "https://files.pythonhosted.org/packages/bc/5e/61537aa6fa977922c6a03253a0e727e6e4a72381a80d63ad8eec350684f2/pillow-12.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bc91a56697869546d1b8f0a3ff35224557ae7f881050e99f615e0119bf934b4e", size = 7125955, upload-time = "2025-10-15T18:21:59.372Z" },
- { url = "https://files.pythonhosted.org/packages/1f/3d/d5033539344ee3cbd9a4d69e12e63ca3a44a739eb2d4c8da350a3d38edd7/pillow-12.0.0-cp311-cp311-win32.whl", hash = "sha256:27f95b12453d165099c84f8a8bfdfd46b9e4bda9e0e4b65f0635430027f55739", size = 6298440, upload-time = "2025-10-15T18:22:00.982Z" },
- { url = "https://files.pythonhosted.org/packages/4d/42/aaca386de5cc8bd8a0254516957c1f265e3521c91515b16e286c662854c4/pillow-12.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:b583dc9070312190192631373c6c8ed277254aa6e6084b74bdd0a6d3b221608e", size = 6999256, upload-time = "2025-10-15T18:22:02.617Z" },
- { url = "https://files.pythonhosted.org/packages/ba/f1/9197c9c2d5708b785f631a6dfbfa8eb3fb9672837cb92ae9af812c13b4ed/pillow-12.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:759de84a33be3b178a64c8ba28ad5c135900359e85fb662bc6e403ad4407791d", size = 2436025, upload-time = "2025-10-15T18:22:04.598Z" },
- { url = "https://files.pythonhosted.org/packages/2c/90/4fcce2c22caf044e660a198d740e7fbc14395619e3cb1abad12192c0826c/pillow-12.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:53561a4ddc36facb432fae7a9d8afbfaf94795414f5cdc5fc52f28c1dca90371", size = 5249377, upload-time = "2025-10-15T18:22:05.993Z" },
- { url = "https://files.pythonhosted.org/packages/fd/e0/ed960067543d080691d47d6938ebccbf3976a931c9567ab2fbfab983a5dd/pillow-12.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:71db6b4c1653045dacc1585c1b0d184004f0d7e694c7b34ac165ca70c0838082", size = 4650343, upload-time = "2025-10-15T18:22:07.718Z" },
- { url = "https://files.pythonhosted.org/packages/e7/a1/f81fdeddcb99c044bf7d6faa47e12850f13cee0849537a7d27eeab5534d4/pillow-12.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2fa5f0b6716fc88f11380b88b31fe591a06c6315e955c096c35715788b339e3f", size = 6232981, upload-time = "2025-10-15T18:22:09.287Z" },
- { url = "https://files.pythonhosted.org/packages/88/e1/9098d3ce341a8750b55b0e00c03f1630d6178f38ac191c81c97a3b047b44/pillow-12.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:82240051c6ca513c616f7f9da06e871f61bfd7805f566275841af15015b8f98d", size = 8041399, upload-time = "2025-10-15T18:22:10.872Z" },
- { url = "https://files.pythonhosted.org/packages/a7/62/a22e8d3b602ae8cc01446d0c57a54e982737f44b6f2e1e019a925143771d/pillow-12.0.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:55f818bd74fe2f11d4d7cbc65880a843c4075e0ac7226bc1a23261dbea531953", size = 6347740, upload-time = "2025-10-15T18:22:12.769Z" },
- { url = "https://files.pythonhosted.org/packages/4f/87/424511bdcd02c8d7acf9f65caa09f291a519b16bd83c3fb3374b3d4ae951/pillow-12.0.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b87843e225e74576437fd5b6a4c2205d422754f84a06942cfaf1dc32243e45a8", size = 7040201, upload-time = "2025-10-15T18:22:14.813Z" },
- { url = "https://files.pythonhosted.org/packages/dc/4d/435c8ac688c54d11755aedfdd9f29c9eeddf68d150fe42d1d3dbd2365149/pillow-12.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c607c90ba67533e1b2355b821fef6764d1dd2cbe26b8c1005ae84f7aea25ff79", size = 6462334, upload-time = "2025-10-15T18:22:16.375Z" },
- { url = "https://files.pythonhosted.org/packages/2b/f2/ad34167a8059a59b8ad10bc5c72d4d9b35acc6b7c0877af8ac885b5f2044/pillow-12.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:21f241bdd5080a15bc86d3466a9f6074a9c2c2b314100dd896ac81ee6db2f1ba", size = 7134162, upload-time = "2025-10-15T18:22:17.996Z" },
- { url = "https://files.pythonhosted.org/packages/0c/b1/a7391df6adacf0a5c2cf6ac1cf1fcc1369e7d439d28f637a847f8803beb3/pillow-12.0.0-cp312-cp312-win32.whl", hash = "sha256:dd333073e0cacdc3089525c7df7d39b211bcdf31fc2824e49d01c6b6187b07d0", size = 6298769, upload-time = "2025-10-15T18:22:19.923Z" },
- { url = "https://files.pythonhosted.org/packages/a2/0b/d87733741526541c909bbf159e338dcace4f982daac6e5a8d6be225ca32d/pillow-12.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:9fe611163f6303d1619bbcb653540a4d60f9e55e622d60a3108be0d5b441017a", size = 7001107, upload-time = "2025-10-15T18:22:21.644Z" },
- { url = "https://files.pythonhosted.org/packages/bc/96/aaa61ce33cc98421fb6088af2a03be4157b1e7e0e87087c888e2370a7f45/pillow-12.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:7dfb439562f234f7d57b1ac6bc8fe7f838a4bd49c79230e0f6a1da93e82f1fad", size = 2436012, upload-time = "2025-10-15T18:22:23.621Z" },
- { url = "https://files.pythonhosted.org/packages/62/f2/de993bb2d21b33a98d031ecf6a978e4b61da207bef02f7b43093774c480d/pillow-12.0.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:0869154a2d0546545cde61d1789a6524319fc1897d9ee31218eae7a60ccc5643", size = 4045493, upload-time = "2025-10-15T18:22:25.758Z" },
- { url = "https://files.pythonhosted.org/packages/0e/b6/bc8d0c4c9f6f111a783d045310945deb769b806d7574764234ffd50bc5ea/pillow-12.0.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:a7921c5a6d31b3d756ec980f2f47c0cfdbce0fc48c22a39347a895f41f4a6ea4", size = 4120461, upload-time = "2025-10-15T18:22:27.286Z" },
- { url = "https://files.pythonhosted.org/packages/5d/57/d60d343709366a353dc56adb4ee1e7d8a2cc34e3fbc22905f4167cfec119/pillow-12.0.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:1ee80a59f6ce048ae13cda1abf7fbd2a34ab9ee7d401c46be3ca685d1999a399", size = 3576912, upload-time = "2025-10-15T18:22:28.751Z" },
- { url = "https://files.pythonhosted.org/packages/a4/a4/a0a31467e3f83b94d37568294b01d22b43ae3c5d85f2811769b9c66389dd/pillow-12.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c50f36a62a22d350c96e49ad02d0da41dbd17ddc2e29750dbdba4323f85eb4a5", size = 5249132, upload-time = "2025-10-15T18:22:30.641Z" },
- { url = "https://files.pythonhosted.org/packages/83/06/48eab21dd561de2914242711434c0c0eb992ed08ff3f6107a5f44527f5e9/pillow-12.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5193fde9a5f23c331ea26d0cf171fbf67e3f247585f50c08b3e205c7aeb4589b", size = 4650099, upload-time = "2025-10-15T18:22:32.73Z" },
- { url = "https://files.pythonhosted.org/packages/fc/bd/69ed99fd46a8dba7c1887156d3572fe4484e3f031405fcc5a92e31c04035/pillow-12.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bde737cff1a975b70652b62d626f7785e0480918dece11e8fef3c0cf057351c3", size = 6230808, upload-time = "2025-10-15T18:22:34.337Z" },
- { url = "https://files.pythonhosted.org/packages/ea/94/8fad659bcdbf86ed70099cb60ae40be6acca434bbc8c4c0d4ef356d7e0de/pillow-12.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a6597ff2b61d121172f5844b53f21467f7082f5fb385a9a29c01414463f93b07", size = 8037804, upload-time = "2025-10-15T18:22:36.402Z" },
- { url = "https://files.pythonhosted.org/packages/20/39/c685d05c06deecfd4e2d1950e9a908aa2ca8bc4e6c3b12d93b9cafbd7837/pillow-12.0.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0b817e7035ea7f6b942c13aa03bb554fc44fea70838ea21f8eb31c638326584e", size = 6345553, upload-time = "2025-10-15T18:22:38.066Z" },
- { url = "https://files.pythonhosted.org/packages/38/57/755dbd06530a27a5ed74f8cb0a7a44a21722ebf318edbe67ddbd7fb28f88/pillow-12.0.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f4f1231b7dec408e8670264ce63e9c71409d9583dd21d32c163e25213ee2a344", size = 7037729, upload-time = "2025-10-15T18:22:39.769Z" },
- { url = "https://files.pythonhosted.org/packages/ca/b6/7e94f4c41d238615674d06ed677c14883103dce1c52e4af16f000338cfd7/pillow-12.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6e51b71417049ad6ab14c49608b4a24d8fb3fe605e5dfabfe523b58064dc3d27", size = 6459789, upload-time = "2025-10-15T18:22:41.437Z" },
- { url = "https://files.pythonhosted.org/packages/9c/14/4448bb0b5e0f22dd865290536d20ec8a23b64e2d04280b89139f09a36bb6/pillow-12.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d120c38a42c234dc9a8c5de7ceaaf899cf33561956acb4941653f8bdc657aa79", size = 7130917, upload-time = "2025-10-15T18:22:43.152Z" },
- { url = "https://files.pythonhosted.org/packages/dd/ca/16c6926cc1c015845745d5c16c9358e24282f1e588237a4c36d2b30f182f/pillow-12.0.0-cp313-cp313-win32.whl", hash = "sha256:4cc6b3b2efff105c6a1656cfe59da4fdde2cda9af1c5e0b58529b24525d0a098", size = 6302391, upload-time = "2025-10-15T18:22:44.753Z" },
- { url = "https://files.pythonhosted.org/packages/6d/2a/dd43dcfd6dae9b6a49ee28a8eedb98c7d5ff2de94a5d834565164667b97b/pillow-12.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:4cf7fed4b4580601c4345ceb5d4cbf5a980d030fd5ad07c4d2ec589f95f09905", size = 7007477, upload-time = "2025-10-15T18:22:46.838Z" },
- { url = "https://files.pythonhosted.org/packages/77/f0/72ea067f4b5ae5ead653053212af05ce3705807906ba3f3e8f58ddf617e6/pillow-12.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:9f0b04c6b8584c2c193babcccc908b38ed29524b29dd464bc8801bf10d746a3a", size = 2435918, upload-time = "2025-10-15T18:22:48.399Z" },
- { url = "https://files.pythonhosted.org/packages/f5/5e/9046b423735c21f0487ea6cb5b10f89ea8f8dfbe32576fe052b5ba9d4e5b/pillow-12.0.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7fa22993bac7b77b78cae22bad1e2a987ddf0d9015c63358032f84a53f23cdc3", size = 5251406, upload-time = "2025-10-15T18:22:49.905Z" },
- { url = "https://files.pythonhosted.org/packages/12/66/982ceebcdb13c97270ef7a56c3969635b4ee7cd45227fa707c94719229c5/pillow-12.0.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f135c702ac42262573fe9714dfe99c944b4ba307af5eb507abef1667e2cbbced", size = 4653218, upload-time = "2025-10-15T18:22:51.587Z" },
- { url = "https://files.pythonhosted.org/packages/16/b3/81e625524688c31859450119bf12674619429cab3119eec0e30a7a1029cb/pillow-12.0.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c85de1136429c524e55cfa4e033b4a7940ac5c8ee4d9401cc2d1bf48154bbc7b", size = 6266564, upload-time = "2025-10-15T18:22:53.215Z" },
- { url = "https://files.pythonhosted.org/packages/98/59/dfb38f2a41240d2408096e1a76c671d0a105a4a8471b1871c6902719450c/pillow-12.0.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:38df9b4bfd3db902c9c2bd369bcacaf9d935b2fff73709429d95cc41554f7b3d", size = 8069260, upload-time = "2025-10-15T18:22:54.933Z" },
- { url = "https://files.pythonhosted.org/packages/dc/3d/378dbea5cd1874b94c312425ca77b0f47776c78e0df2df751b820c8c1d6c/pillow-12.0.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7d87ef5795da03d742bf49439f9ca4d027cde49c82c5371ba52464aee266699a", size = 6379248, upload-time = "2025-10-15T18:22:56.605Z" },
- { url = "https://files.pythonhosted.org/packages/84/b0/d525ef47d71590f1621510327acec75ae58c721dc071b17d8d652ca494d8/pillow-12.0.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aff9e4d82d082ff9513bdd6acd4f5bd359f5b2c870907d2b0a9c5e10d40c88fe", size = 7066043, upload-time = "2025-10-15T18:22:58.53Z" },
- { url = "https://files.pythonhosted.org/packages/61/2c/aced60e9cf9d0cde341d54bf7932c9ffc33ddb4a1595798b3a5150c7ec4e/pillow-12.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:8d8ca2b210ada074d57fcee40c30446c9562e542fc46aedc19baf758a93532ee", size = 6490915, upload-time = "2025-10-15T18:23:00.582Z" },
- { url = "https://files.pythonhosted.org/packages/ef/26/69dcb9b91f4e59f8f34b2332a4a0a951b44f547c4ed39d3e4dcfcff48f89/pillow-12.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:99a7f72fb6249302aa62245680754862a44179b545ded638cf1fef59befb57ef", size = 7157998, upload-time = "2025-10-15T18:23:02.627Z" },
- { url = "https://files.pythonhosted.org/packages/61/2b/726235842220ca95fa441ddf55dd2382b52ab5b8d9c0596fe6b3f23dafe8/pillow-12.0.0-cp313-cp313t-win32.whl", hash = "sha256:4078242472387600b2ce8d93ade8899c12bf33fa89e55ec89fe126e9d6d5d9e9", size = 6306201, upload-time = "2025-10-15T18:23:04.709Z" },
- { url = "https://files.pythonhosted.org/packages/c0/3d/2afaf4e840b2df71344ababf2f8edd75a705ce500e5dc1e7227808312ae1/pillow-12.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2c54c1a783d6d60595d3514f0efe9b37c8808746a66920315bfd34a938d7994b", size = 7013165, upload-time = "2025-10-15T18:23:06.46Z" },
- { url = "https://files.pythonhosted.org/packages/6f/75/3fa09aa5cf6ed04bee3fa575798ddf1ce0bace8edb47249c798077a81f7f/pillow-12.0.0-cp313-cp313t-win_arm64.whl", hash = "sha256:26d9f7d2b604cd23aba3e9faf795787456ac25634d82cd060556998e39c6fa47", size = 2437834, upload-time = "2025-10-15T18:23:08.194Z" },
- { url = "https://files.pythonhosted.org/packages/54/2a/9a8c6ba2c2c07b71bec92cf63e03370ca5e5f5c5b119b742bcc0cde3f9c5/pillow-12.0.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:beeae3f27f62308f1ddbcfb0690bf44b10732f2ef43758f169d5e9303165d3f9", size = 4045531, upload-time = "2025-10-15T18:23:10.121Z" },
- { url = "https://files.pythonhosted.org/packages/84/54/836fdbf1bfb3d66a59f0189ff0b9f5f666cee09c6188309300df04ad71fa/pillow-12.0.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:d4827615da15cd59784ce39d3388275ec093ae3ee8d7f0c089b76fa87af756c2", size = 4120554, upload-time = "2025-10-15T18:23:12.14Z" },
- { url = "https://files.pythonhosted.org/packages/0d/cd/16aec9f0da4793e98e6b54778a5fbce4f375c6646fe662e80600b8797379/pillow-12.0.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:3e42edad50b6909089750e65c91aa09aaf1e0a71310d383f11321b27c224ed8a", size = 3576812, upload-time = "2025-10-15T18:23:13.962Z" },
- { url = "https://files.pythonhosted.org/packages/f6/b7/13957fda356dc46339298b351cae0d327704986337c3c69bb54628c88155/pillow-12.0.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:e5d8efac84c9afcb40914ab49ba063d94f5dbdf5066db4482c66a992f47a3a3b", size = 5252689, upload-time = "2025-10-15T18:23:15.562Z" },
- { url = "https://files.pythonhosted.org/packages/fc/f5/eae31a306341d8f331f43edb2e9122c7661b975433de5e447939ae61c5da/pillow-12.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:266cd5f2b63ff316d5a1bba46268e603c9caf5606d44f38c2873c380950576ad", size = 4650186, upload-time = "2025-10-15T18:23:17.379Z" },
- { url = "https://files.pythonhosted.org/packages/86/62/2a88339aa40c4c77e79108facbd307d6091e2c0eb5b8d3cf4977cfca2fe6/pillow-12.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:58eea5ebe51504057dd95c5b77d21700b77615ab0243d8152793dc00eb4faf01", size = 6230308, upload-time = "2025-10-15T18:23:18.971Z" },
- { url = "https://files.pythonhosted.org/packages/c7/33/5425a8992bcb32d1cb9fa3dd39a89e613d09a22f2c8083b7bf43c455f760/pillow-12.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f13711b1a5ba512d647a0e4ba79280d3a9a045aaf7e0cc6fbe96b91d4cdf6b0c", size = 8039222, upload-time = "2025-10-15T18:23:20.909Z" },
- { url = "https://files.pythonhosted.org/packages/d8/61/3f5d3b35c5728f37953d3eec5b5f3e77111949523bd2dd7f31a851e50690/pillow-12.0.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6846bd2d116ff42cba6b646edf5bf61d37e5cbd256425fa089fee4ff5c07a99e", size = 6346657, upload-time = "2025-10-15T18:23:23.077Z" },
- { url = "https://files.pythonhosted.org/packages/3a/be/ee90a3d79271227e0f0a33c453531efd6ed14b2e708596ba5dd9be948da3/pillow-12.0.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c98fa880d695de164b4135a52fd2e9cd7b7c90a9d8ac5e9e443a24a95ef9248e", size = 7038482, upload-time = "2025-10-15T18:23:25.005Z" },
- { url = "https://files.pythonhosted.org/packages/44/34/a16b6a4d1ad727de390e9bd9f19f5f669e079e5826ec0f329010ddea492f/pillow-12.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa3ed2a29a9e9d2d488b4da81dcb54720ac3104a20bf0bd273f1e4648aff5af9", size = 6461416, upload-time = "2025-10-15T18:23:27.009Z" },
- { url = "https://files.pythonhosted.org/packages/b6/39/1aa5850d2ade7d7ba9f54e4e4c17077244ff7a2d9e25998c38a29749eb3f/pillow-12.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d034140032870024e6b9892c692fe2968493790dd57208b2c37e3fb35f6df3ab", size = 7131584, upload-time = "2025-10-15T18:23:29.752Z" },
- { url = "https://files.pythonhosted.org/packages/bf/db/4fae862f8fad0167073a7733973bfa955f47e2cac3dc3e3e6257d10fab4a/pillow-12.0.0-cp314-cp314-win32.whl", hash = "sha256:1b1b133e6e16105f524a8dec491e0586d072948ce15c9b914e41cdadd209052b", size = 6400621, upload-time = "2025-10-15T18:23:32.06Z" },
- { url = "https://files.pythonhosted.org/packages/2b/24/b350c31543fb0107ab2599464d7e28e6f856027aadda995022e695313d94/pillow-12.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:8dc232e39d409036af549c86f24aed8273a40ffa459981146829a324e0848b4b", size = 7142916, upload-time = "2025-10-15T18:23:34.71Z" },
- { url = "https://files.pythonhosted.org/packages/0f/9b/0ba5a6fd9351793996ef7487c4fdbde8d3f5f75dbedc093bb598648fddf0/pillow-12.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:d52610d51e265a51518692045e372a4c363056130d922a7351429ac9f27e70b0", size = 2523836, upload-time = "2025-10-15T18:23:36.967Z" },
- { url = "https://files.pythonhosted.org/packages/f5/7a/ceee0840aebc579af529b523d530840338ecf63992395842e54edc805987/pillow-12.0.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:1979f4566bb96c1e50a62d9831e2ea2d1211761e5662afc545fa766f996632f6", size = 5255092, upload-time = "2025-10-15T18:23:38.573Z" },
- { url = "https://files.pythonhosted.org/packages/44/76/20776057b4bfd1aef4eeca992ebde0f53a4dce874f3ae693d0ec90a4f79b/pillow-12.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b2e4b27a6e15b04832fe9bf292b94b5ca156016bbc1ea9c2c20098a0320d6cf6", size = 4653158, upload-time = "2025-10-15T18:23:40.238Z" },
- { url = "https://files.pythonhosted.org/packages/82/3f/d9ff92ace07be8836b4e7e87e6a4c7a8318d47c2f1463ffcf121fc57d9cb/pillow-12.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fb3096c30df99fd01c7bf8e544f392103d0795b9f98ba71a8054bcbf56b255f1", size = 6267882, upload-time = "2025-10-15T18:23:42.434Z" },
- { url = "https://files.pythonhosted.org/packages/9f/7a/4f7ff87f00d3ad33ba21af78bfcd2f032107710baf8280e3722ceec28cda/pillow-12.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7438839e9e053ef79f7112c881cef684013855016f928b168b81ed5835f3e75e", size = 8071001, upload-time = "2025-10-15T18:23:44.29Z" },
- { url = "https://files.pythonhosted.org/packages/75/87/fcea108944a52dad8cca0715ae6247e271eb80459364a98518f1e4f480c1/pillow-12.0.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d5c411a8eaa2299322b647cd932586b1427367fd3184ffbb8f7a219ea2041ca", size = 6380146, upload-time = "2025-10-15T18:23:46.065Z" },
- { url = "https://files.pythonhosted.org/packages/91/52/0d31b5e571ef5fd111d2978b84603fce26aba1b6092f28e941cb46570745/pillow-12.0.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d7e091d464ac59d2c7ad8e7e08105eaf9dafbc3883fd7265ffccc2baad6ac925", size = 7067344, upload-time = "2025-10-15T18:23:47.898Z" },
- { url = "https://files.pythonhosted.org/packages/7b/f4/2dd3d721f875f928d48e83bb30a434dee75a2531bca839bb996bb0aa5a91/pillow-12.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:792a2c0be4dcc18af9d4a2dfd8a11a17d5e25274a1062b0ec1c2d79c76f3e7f8", size = 6491864, upload-time = "2025-10-15T18:23:49.607Z" },
- { url = "https://files.pythonhosted.org/packages/30/4b/667dfcf3d61fc309ba5a15b141845cece5915e39b99c1ceab0f34bf1d124/pillow-12.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:afbefa430092f71a9593a99ab6a4e7538bc9eabbf7bf94f91510d3503943edc4", size = 7158911, upload-time = "2025-10-15T18:23:51.351Z" },
- { url = "https://files.pythonhosted.org/packages/a2/2f/16cabcc6426c32218ace36bf0d55955e813f2958afddbf1d391849fee9d1/pillow-12.0.0-cp314-cp314t-win32.whl", hash = "sha256:3830c769decf88f1289680a59d4f4c46c72573446352e2befec9a8512104fa52", size = 6408045, upload-time = "2025-10-15T18:23:53.177Z" },
- { url = "https://files.pythonhosted.org/packages/35/73/e29aa0c9c666cf787628d3f0dcf379f4791fba79f4936d02f8b37165bdf8/pillow-12.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:905b0365b210c73afb0ebe9101a32572152dfd1c144c7e28968a331b9217b94a", size = 7148282, upload-time = "2025-10-15T18:23:55.316Z" },
- { url = "https://files.pythonhosted.org/packages/c1/70/6b41bdcddf541b437bbb9f47f94d2db5d9ddef6c37ccab8c9107743748a4/pillow-12.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:99353a06902c2e43b43e8ff74ee65a7d90307d82370604746738a1e0661ccca7", size = 2525630, upload-time = "2025-10-15T18:23:57.149Z" },
- { url = "https://files.pythonhosted.org/packages/1d/b3/582327e6c9f86d037b63beebe981425d6811104cb443e8193824ef1a2f27/pillow-12.0.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b22bd8c974942477156be55a768f7aa37c46904c175be4e158b6a86e3a6b7ca8", size = 5215068, upload-time = "2025-10-15T18:23:59.594Z" },
- { url = "https://files.pythonhosted.org/packages/fd/d6/67748211d119f3b6540baf90f92fae73ae51d5217b171b0e8b5f7e5d558f/pillow-12.0.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:805ebf596939e48dbb2e4922a1d3852cfc25c38160751ce02da93058b48d252a", size = 4614994, upload-time = "2025-10-15T18:24:01.669Z" },
- { url = "https://files.pythonhosted.org/packages/2d/e1/f8281e5d844c41872b273b9f2c34a4bf64ca08905668c8ae730eedc7c9fa/pillow-12.0.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cae81479f77420d217def5f54b5b9d279804d17e982e0f2fa19b1d1e14ab5197", size = 5246639, upload-time = "2025-10-15T18:24:03.403Z" },
- { url = "https://files.pythonhosted.org/packages/94/5a/0d8ab8ffe8a102ff5df60d0de5af309015163bf710c7bb3e8311dd3b3ad0/pillow-12.0.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:aeaefa96c768fc66818730b952a862235d68825c178f1b3ffd4efd7ad2edcb7c", size = 6986839, upload-time = "2025-10-15T18:24:05.344Z" },
- { url = "https://files.pythonhosted.org/packages/20/2e/3434380e8110b76cd9eb00a363c484b050f949b4bbe84ba770bb8508a02c/pillow-12.0.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:09f2d0abef9e4e2f349305a4f8cc784a8a6c2f58a8c4892eea13b10a943bd26e", size = 5313505, upload-time = "2025-10-15T18:24:07.137Z" },
- { url = "https://files.pythonhosted.org/packages/57/ca/5a9d38900d9d74785141d6580950fe705de68af735ff6e727cb911b64740/pillow-12.0.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bdee52571a343d721fb2eb3b090a82d959ff37fc631e3f70422e0c2e029f3e76", size = 5963654, upload-time = "2025-10-15T18:24:09.579Z" },
- { url = "https://files.pythonhosted.org/packages/95/7e/f896623c3c635a90537ac093c6a618ebe1a90d87206e42309cb5d98a1b9e/pillow-12.0.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:b290fd8aa38422444d4b50d579de197557f182ef1068b75f5aa8558638b8d0a5", size = 6997850, upload-time = "2025-10-15T18:24:11.495Z" },
+version = "12.2.0"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/8c/21/c2bcdd5906101a30244eaffc1b6e6ce71a31bd0742a01eb89e660ebfac2d/pillow-12.2.0.tar.gz", hash = "sha256:a830b1a40919539d07806aa58e1b114df53ddd43213d9c8b75847eee6c0182b5", size = 46987819, upload-time = "2026-04-01T14:46:17.687Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/68/e1/748f5663efe6edcfc4e74b2b93edfb9b8b99b67f21a854c3ae416500a2d9/pillow-12.2.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:8be29e59487a79f173507c30ddf57e733a357f67881430449bb32614075a40ab", size = 5354347, upload-time = "2026-04-01T14:42:44.255Z" },
+ { url = "https://files.pythonhosted.org/packages/47/a1/d5ff69e747374c33a3b53b9f98cca7889fce1fd03d79cdc4e1bccc6c5a87/pillow-12.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:71cde9a1e1551df7d34a25462fc60325e8a11a82cc2e2f54578e5e9a1e153d65", size = 4695873, upload-time = "2026-04-01T14:42:46.452Z" },
+ { url = "https://files.pythonhosted.org/packages/df/21/e3fbdf54408a973c7f7f89a23b2cb97a7ef30c61ab4142af31eee6aebc88/pillow-12.2.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f490f9368b6fc026f021db16d7ec2fbf7d89e2edb42e8ec09d2c60505f5729c7", size = 6280168, upload-time = "2026-04-01T14:42:49.228Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/f1/00b7278c7dd52b17ad4329153748f87b6756ec195ff786c2bdf12518337d/pillow-12.2.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8bd7903a5f2a4545f6fd5935c90058b89d30045568985a71c79f5fd6edf9b91e", size = 8088188, upload-time = "2026-04-01T14:42:51.735Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/cf/220a5994ef1b10e70e85748b75649d77d506499352be135a4989c957b701/pillow-12.2.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3997232e10d2920a68d25191392e3a4487d8183039e1c74c2297f00ed1c50705", size = 6394401, upload-time = "2026-04-01T14:42:54.343Z" },
+ { url = "https://files.pythonhosted.org/packages/e9/bd/e51a61b1054f09437acfbc2ff9106c30d1eb76bc1453d428399946781253/pillow-12.2.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e74473c875d78b8e9d5da2a70f7099549f9eb37ded4e2f6a463e60125bccd176", size = 7079655, upload-time = "2026-04-01T14:42:56.954Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/3d/45132c57d5fb4b5744567c3817026480ac7fc3ce5d4c47902bc0e7f6f853/pillow-12.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:56a3f9c60a13133a98ecff6197af34d7824de9b7b38c3654861a725c970c197b", size = 6503105, upload-time = "2026-04-01T14:42:59.847Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/2e/9df2fc1e82097b1df3dce58dc43286aa01068e918c07574711fcc53e6fb4/pillow-12.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:90e6f81de50ad6b534cab6e5aef77ff6e37722b2f5d908686f4a5c9eba17a909", size = 7203402, upload-time = "2026-04-01T14:43:02.664Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/2e/2941e42858ebb67e50ae741473de81c2984e6eff7b397017623c676e2e8d/pillow-12.2.0-cp311-cp311-win32.whl", hash = "sha256:8c984051042858021a54926eb597d6ee3012393ce9c181814115df4c60b9a808", size = 6378149, upload-time = "2026-04-01T14:43:05.274Z" },
+ { url = "https://files.pythonhosted.org/packages/69/42/836b6f3cd7f3e5fa10a1f1a5420447c17966044c8fbf589cc0452d5502db/pillow-12.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:6e6b2a0c538fc200b38ff9eb6628228b77908c319a005815f2dde585a0664b60", size = 7082626, upload-time = "2026-04-01T14:43:08.557Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/88/549194b5d6f1f494b485e493edc6693c0a16f4ada488e5bd974ed1f42fad/pillow-12.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:9a8a34cc89c67a65ea7437ce257cea81a9dad65b29805f3ecee8c8fe8ff25ffe", size = 2463531, upload-time = "2026-04-01T14:43:10.743Z" },
+ { url = "https://files.pythonhosted.org/packages/58/be/7482c8a5ebebbc6470b3eb791812fff7d5e0216c2be3827b30b8bb6603ed/pillow-12.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2d192a155bbcec180f8564f693e6fd9bccff5a7af9b32e2e4bf8c9c69dbad6b5", size = 5308279, upload-time = "2026-04-01T14:43:13.246Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/95/0a351b9289c2b5cbde0bacd4a83ebc44023e835490a727b2a3bd60ddc0f4/pillow-12.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f3f40b3c5a968281fd507d519e444c35f0ff171237f4fdde090dd60699458421", size = 4695490, upload-time = "2026-04-01T14:43:15.584Z" },
+ { url = "https://files.pythonhosted.org/packages/de/af/4e8e6869cbed569d43c416fad3dc4ecb944cb5d9492defaed89ddd6fe871/pillow-12.2.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:03e7e372d5240cc23e9f07deca4d775c0817bffc641b01e9c3af208dbd300987", size = 6284462, upload-time = "2026-04-01T14:43:18.268Z" },
+ { url = "https://files.pythonhosted.org/packages/e9/9e/c05e19657fd57841e476be1ab46c4d501bffbadbafdc31a6d665f8b737b6/pillow-12.2.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b86024e52a1b269467a802258c25521e6d742349d760728092e1bc2d135b4d76", size = 8094744, upload-time = "2026-04-01T14:43:20.716Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/54/1789c455ed10176066b6e7e6da1b01e50e36f94ba584dc68d9eebfe9156d/pillow-12.2.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7371b48c4fa448d20d2714c9a1f775a81155050d383333e0a6c15b1123dda005", size = 6398371, upload-time = "2026-04-01T14:43:23.443Z" },
+ { url = "https://files.pythonhosted.org/packages/43/e3/fdc657359e919462369869f1c9f0e973f353f9a9ee295a39b1fea8ee1a77/pillow-12.2.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62f5409336adb0663b7caa0da5c7d9e7bdbaae9ce761d34669420c2a801b2780", size = 7087215, upload-time = "2026-04-01T14:43:26.758Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/f8/2f6825e441d5b1959d2ca5adec984210f1ec086435b0ed5f52c19b3b8a6e/pillow-12.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:01afa7cf67f74f09523699b4e88c73fb55c13346d212a59a2db1f86b0a63e8c5", size = 6509783, upload-time = "2026-04-01T14:43:29.56Z" },
+ { url = "https://files.pythonhosted.org/packages/67/f9/029a27095ad20f854f9dba026b3ea6428548316e057e6fc3545409e86651/pillow-12.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc3d34d4a8fbec3e88a79b92e5465e0f9b842b628675850d860b8bd300b159f5", size = 7212112, upload-time = "2026-04-01T14:43:32.091Z" },
+ { url = "https://files.pythonhosted.org/packages/be/42/025cfe05d1be22dbfdb4f264fe9de1ccda83f66e4fc3aac94748e784af04/pillow-12.2.0-cp312-cp312-win32.whl", hash = "sha256:58f62cc0f00fd29e64b29f4fd923ffdb3859c9f9e6105bfc37ba1d08994e8940", size = 6378489, upload-time = "2026-04-01T14:43:34.601Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/7b/25a221d2c761c6a8ae21bfa3874988ff2583e19cf8a27bf2fee358df7942/pillow-12.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:7f84204dee22a783350679a0333981df803dac21a0190d706a50475e361c93f5", size = 7084129, upload-time = "2026-04-01T14:43:37.213Z" },
+ { url = "https://files.pythonhosted.org/packages/10/e1/542a474affab20fd4a0f1836cb234e8493519da6b76899e30bcc5d990b8b/pillow-12.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:af73337013e0b3b46f175e79492d96845b16126ddf79c438d7ea7ff27783a414", size = 2463612, upload-time = "2026-04-01T14:43:39.421Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/01/53d10cf0dbad820a8db274d259a37ba50b88b24768ddccec07355382d5ad/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:8297651f5b5679c19968abefd6bb84d95fe30ef712eb1b2d9b2d31ca61267f4c", size = 4100837, upload-time = "2026-04-01T14:43:41.506Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/98/f3a6657ecb698c937f6c76ee564882945f29b79bad496abcba0e84659ec5/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:50d8520da2a6ce0af445fa6d648c4273c3eeefbc32d7ce049f22e8b5c3daecc2", size = 4176528, upload-time = "2026-04-01T14:43:43.773Z" },
+ { url = "https://files.pythonhosted.org/packages/69/bc/8986948f05e3ea490b8442ea1c1d4d990b24a7e43d8a51b2c7d8b1dced36/pillow-12.2.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:766cef22385fa1091258ad7e6216792b156dc16d8d3fa607e7545b2b72061f1c", size = 3640401, upload-time = "2026-04-01T14:43:45.87Z" },
+ { url = "https://files.pythonhosted.org/packages/34/46/6c717baadcd62bc8ed51d238d521ab651eaa74838291bda1f86fe1f864c9/pillow-12.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5d2fd0fa6b5d9d1de415060363433f28da8b1526c1c129020435e186794b3795", size = 5308094, upload-time = "2026-04-01T14:43:48.438Z" },
+ { url = "https://files.pythonhosted.org/packages/71/43/905a14a8b17fdb1ccb58d282454490662d2cb89a6bfec26af6d3520da5ec/pillow-12.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56b25336f502b6ed02e889f4ece894a72612fe885889a6e8c4c80239ff6e5f5f", size = 4695402, upload-time = "2026-04-01T14:43:51.292Z" },
+ { url = "https://files.pythonhosted.org/packages/73/dd/42107efcb777b16fa0393317eac58f5b5cf30e8392e266e76e51cff28c3d/pillow-12.2.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f1c943e96e85df3d3478f7b691f229887e143f81fedab9b20205349ab04d73ed", size = 6280005, upload-time = "2026-04-01T14:43:54.242Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/68/b93e09e5e8549019e61acf49f65b1a8530765a7f812c77a7461bca7e4494/pillow-12.2.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:03f6fab9219220f041c74aeaa2939ff0062bd5c364ba9ce037197f4c6d498cd9", size = 8090669, upload-time = "2026-04-01T14:43:57.335Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/6e/3ccb54ce8ec4ddd1accd2d89004308b7b0b21c4ac3d20fa70af4760a4330/pillow-12.2.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cdfebd752ec52bf5bb4e35d9c64b40826bc5b40a13df7c3cda20a2c03a0f5ed", size = 6395194, upload-time = "2026-04-01T14:43:59.864Z" },
+ { url = "https://files.pythonhosted.org/packages/67/ee/21d4e8536afd1a328f01b359b4d3997b291ffd35a237c877b331c1c3b71c/pillow-12.2.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eedf4b74eda2b5a4b2b2fb4c006d6295df3bf29e459e198c90ea48e130dc75c3", size = 7082423, upload-time = "2026-04-01T14:44:02.74Z" },
+ { url = "https://files.pythonhosted.org/packages/78/5f/e9f86ab0146464e8c133fe85df987ed9e77e08b29d8d35f9f9f4d6f917ba/pillow-12.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:00a2865911330191c0b818c59103b58a5e697cae67042366970a6b6f1b20b7f9", size = 6505667, upload-time = "2026-04-01T14:44:05.381Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/1e/409007f56a2fdce61584fd3acbc2bbc259857d555196cedcadc68c015c82/pillow-12.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1e1757442ed87f4912397c6d35a0db6a7b52592156014706f17658ff58bbf795", size = 7208580, upload-time = "2026-04-01T14:44:08.39Z" },
+ { url = "https://files.pythonhosted.org/packages/23/c4/7349421080b12fb35414607b8871e9534546c128a11965fd4a7002ccfbee/pillow-12.2.0-cp313-cp313-win32.whl", hash = "sha256:144748b3af2d1b358d41286056d0003f47cb339b8c43a9ea42f5fea4d8c66b6e", size = 6375896, upload-time = "2026-04-01T14:44:11.197Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/82/8a3739a5e470b3c6cbb1d21d315800d8e16bff503d1f16b03a4ec3212786/pillow-12.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:390ede346628ccc626e5730107cde16c42d3836b89662a115a921f28440e6a3b", size = 7081266, upload-time = "2026-04-01T14:44:13.947Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/25/f968f618a062574294592f668218f8af564830ccebdd1fa6200f598e65c5/pillow-12.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:8023abc91fba39036dbce14a7d6535632f99c0b857807cbbbf21ecc9f4717f06", size = 2463508, upload-time = "2026-04-01T14:44:16.312Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/a4/b342930964e3cb4dce5038ae34b0eab4653334995336cd486c5a8c25a00c/pillow-12.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:042db20a421b9bafecc4b84a8b6e444686bd9d836c7fd24542db3e7df7baad9b", size = 5309927, upload-time = "2026-04-01T14:44:18.89Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/de/23198e0a65a9cf06123f5435a5d95cea62a635697f8f03d134d3f3a96151/pillow-12.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:dd025009355c926a84a612fecf58bb315a3f6814b17ead51a8e48d3823d9087f", size = 4698624, upload-time = "2026-04-01T14:44:21.115Z" },
+ { url = "https://files.pythonhosted.org/packages/01/a6/1265e977f17d93ea37aa28aa81bad4fa597933879fac2520d24e021c8da3/pillow-12.2.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:88ddbc66737e277852913bd1e07c150cc7bb124539f94c4e2df5344494e0a612", size = 6321252, upload-time = "2026-04-01T14:44:23.663Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/83/5982eb4a285967baa70340320be9f88e57665a387e3a53a7f0db8231a0cd/pillow-12.2.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d362d1878f00c142b7e1a16e6e5e780f02be8195123f164edf7eddd911eefe7c", size = 8126550, upload-time = "2026-04-01T14:44:26.772Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/48/6ffc514adce69f6050d0753b1a18fd920fce8cac87620d5a31231b04bfc5/pillow-12.2.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c727a6d53cb0018aadd8018c2b938376af27914a68a492f59dfcaca650d5eea", size = 6433114, upload-time = "2026-04-01T14:44:29.615Z" },
+ { url = "https://files.pythonhosted.org/packages/36/a3/f9a77144231fb8d40ee27107b4463e205fa4677e2ca2548e14da5cf18dce/pillow-12.2.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:efd8c21c98c5cc60653bcb311bef2ce0401642b7ce9d09e03a7da87c878289d4", size = 7115667, upload-time = "2026-04-01T14:44:32.773Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/fc/ac4ee3041e7d5a565e1c4fd72a113f03b6394cc72ab7089d27608f8aaccb/pillow-12.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9f08483a632889536b8139663db60f6724bfcb443c96f1b18855860d7d5c0fd4", size = 6538966, upload-time = "2026-04-01T14:44:35.252Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/a8/27fb307055087f3668f6d0a8ccb636e7431d56ed0750e07a60547b1e083e/pillow-12.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dac8d77255a37e81a2efcbd1fc05f1c15ee82200e6c240d7e127e25e365c39ea", size = 7238241, upload-time = "2026-04-01T14:44:37.875Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/4b/926ab182c07fccae9fcb120043464e1ff1564775ec8864f21a0ebce6ac25/pillow-12.2.0-cp313-cp313t-win32.whl", hash = "sha256:ee3120ae9dff32f121610bb08e4313be87e03efeadfc6c0d18f89127e24d0c24", size = 6379592, upload-time = "2026-04-01T14:44:40.336Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/c4/f9e476451a098181b30050cc4c9a3556b64c02cf6497ea421ac047e89e4b/pillow-12.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:325ca0528c6788d2a6c3d40e3568639398137346c3d6e66bb61db96b96511c98", size = 7085542, upload-time = "2026-04-01T14:44:43.251Z" },
+ { url = "https://files.pythonhosted.org/packages/00/a4/285f12aeacbe2d6dc36c407dfbbe9e96d4a80b0fb710a337f6d2ad978c75/pillow-12.2.0-cp313-cp313t-win_arm64.whl", hash = "sha256:2e5a76d03a6c6dcef67edabda7a52494afa4035021a79c8558e14af25313d453", size = 2465765, upload-time = "2026-04-01T14:44:45.996Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/98/4595daa2365416a86cb0d495248a393dfc84e96d62ad080c8546256cb9c0/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:3adc9215e8be0448ed6e814966ecf3d9952f0ea40eb14e89a102b87f450660d8", size = 4100848, upload-time = "2026-04-01T14:44:48.48Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/79/40184d464cf89f6663e18dfcf7ca21aae2491fff1a16127681bf1fa9b8cf/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:6a9adfc6d24b10f89588096364cc726174118c62130c817c2837c60cf08a392b", size = 4176515, upload-time = "2026-04-01T14:44:51.353Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/63/703f86fd4c422a9cf722833670f4f71418fb116b2853ff7da722ea43f184/pillow-12.2.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:6a6e67ea2e6feda684ed370f9a1c52e7a243631c025ba42149a2cc5934dec295", size = 3640159, upload-time = "2026-04-01T14:44:53.588Z" },
+ { url = "https://files.pythonhosted.org/packages/71/e0/fb22f797187d0be2270f83500aab851536101b254bfa1eae10795709d283/pillow-12.2.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2bb4a8d594eacdfc59d9e5ad972aa8afdd48d584ffd5f13a937a664c3e7db0ed", size = 5312185, upload-time = "2026-04-01T14:44:56.039Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/8c/1a9e46228571de18f8e28f16fabdfc20212a5d019f3e3303452b3f0a580d/pillow-12.2.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:80b2da48193b2f33ed0c32c38140f9d3186583ce7d516526d462645fd98660ae", size = 4695386, upload-time = "2026-04-01T14:44:58.663Z" },
+ { url = "https://files.pythonhosted.org/packages/70/62/98f6b7f0c88b9addd0e87c217ded307b36be024d4ff8869a812b241d1345/pillow-12.2.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22db17c68434de69d8ecfc2fe821569195c0c373b25cccb9cbdacf2c6e53c601", size = 6280384, upload-time = "2026-04-01T14:45:01.5Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/03/688747d2e91cfbe0e64f316cd2e8005698f76ada3130d0194664174fa5de/pillow-12.2.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7b14cc0106cd9aecda615dd6903840a058b4700fcb817687d0ee4fc8b6e389be", size = 8091599, upload-time = "2026-04-01T14:45:04.5Z" },
+ { url = "https://files.pythonhosted.org/packages/f6/35/577e22b936fcdd66537329b33af0b4ccfefaeabd8aec04b266528cddb33c/pillow-12.2.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cbeb542b2ebc6fcdacabf8aca8c1a97c9b3ad3927d46b8723f9d4f033288a0f", size = 6396021, upload-time = "2026-04-01T14:45:07.117Z" },
+ { url = "https://files.pythonhosted.org/packages/11/8d/d2532ad2a603ca2b93ad9f5135732124e57811d0168155852f37fbce2458/pillow-12.2.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4bfd07bc812fbd20395212969e41931001fd59eb55a60658b0e5710872e95286", size = 7083360, upload-time = "2026-04-01T14:45:09.763Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/26/d325f9f56c7e039034897e7380e9cc202b1e368bfd04d4cbe6a441f02885/pillow-12.2.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9aba9a17b623ef750a4d11b742cbafffeb48a869821252b30ee21b5e91392c50", size = 6507628, upload-time = "2026-04-01T14:45:12.378Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/f7/769d5632ffb0988f1c5e7660b3e731e30f7f8ec4318e94d0a5d674eb65a4/pillow-12.2.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:deede7c263feb25dba4e82ea23058a235dcc2fe1f6021025dc71f2b618e26104", size = 7209321, upload-time = "2026-04-01T14:45:15.122Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/7a/c253e3c645cd47f1aceea6a8bacdba9991bf45bb7dfe927f7c893e89c93c/pillow-12.2.0-cp314-cp314-win32.whl", hash = "sha256:632ff19b2778e43162304d50da0181ce24ac5bb8180122cbe1bf4673428328c7", size = 6479723, upload-time = "2026-04-01T14:45:17.797Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/8b/601e6566b957ca50e28725cb6c355c59c2c8609751efbecd980db44e0349/pillow-12.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:4e6c62e9d237e9b65fac06857d511e90d8461a32adcc1b9065ea0c0fa3a28150", size = 7217400, upload-time = "2026-04-01T14:45:20.529Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/94/220e46c73065c3e2951bb91c11a1fb636c8c9ad427ac3ce7d7f3359b9b2f/pillow-12.2.0-cp314-cp314-win_arm64.whl", hash = "sha256:b1c1fbd8a5a1af3412a0810d060a78b5136ec0836c8a4ef9aa11807f2a22f4e1", size = 2554835, upload-time = "2026-04-01T14:45:23.162Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/ab/1b426a3974cb0e7da5c29ccff4807871d48110933a57207b5a676cccc155/pillow-12.2.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:57850958fe9c751670e49b2cecf6294acc99e562531f4bd317fa5ddee2068463", size = 5314225, upload-time = "2026-04-01T14:45:25.637Z" },
+ { url = "https://files.pythonhosted.org/packages/19/1e/dce46f371be2438eecfee2a1960ee2a243bbe5e961890146d2dee1ff0f12/pillow-12.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d5d38f1411c0ed9f97bcb49b7bd59b6b7c314e0e27420e34d99d844b9ce3b6f3", size = 4698541, upload-time = "2026-04-01T14:45:28.355Z" },
+ { url = "https://files.pythonhosted.org/packages/55/c3/7fbecf70adb3a0c33b77a300dc52e424dc22ad8cdc06557a2e49523b703d/pillow-12.2.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5c0a9f29ca8e79f09de89293f82fc9b0270bb4af1d58bc98f540cc4aedf03166", size = 6322251, upload-time = "2026-04-01T14:45:30.924Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/3c/7fbc17cfb7e4fe0ef1642e0abc17fc6c94c9f7a16be41498e12e2ba60408/pillow-12.2.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1610dd6c61621ae1cf811bef44d77e149ce3f7b95afe66a4512f8c59f25d9ebe", size = 8127807, upload-time = "2026-04-01T14:45:33.908Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/c3/a8ae14d6defd2e448493ff512fae903b1e9bd40b72efb6ec55ce0048c8ce/pillow-12.2.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a34329707af4f73cf1782a36cd2289c0368880654a2c11f027bcee9052d35dd", size = 6433935, upload-time = "2026-04-01T14:45:36.623Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/32/2880fb3a074847ac159d8f902cb43278a61e85f681661e7419e6596803ed/pillow-12.2.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e9c4f5b3c546fa3458a29ab22646c1c6c787ea8f5ef51300e5a60300736905e", size = 7116720, upload-time = "2026-04-01T14:45:39.258Z" },
+ { url = "https://files.pythonhosted.org/packages/46/87/495cc9c30e0129501643f24d320076f4cc54f718341df18cc70ec94c44e1/pillow-12.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fb043ee2f06b41473269765c2feae53fc2e2fbf96e5e22ca94fb5ad677856f06", size = 6540498, upload-time = "2026-04-01T14:45:41.879Z" },
+ { url = "https://files.pythonhosted.org/packages/18/53/773f5edca692009d883a72211b60fdaf8871cbef075eaa9d577f0a2f989e/pillow-12.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f278f034eb75b4e8a13a54a876cc4a5ab39173d2cdd93a638e1b467fc545ac43", size = 7239413, upload-time = "2026-04-01T14:45:44.705Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/e4/4b64a97d71b2a83158134abbb2f5bd3f8a2ea691361282f010998f339ec7/pillow-12.2.0-cp314-cp314t-win32.whl", hash = "sha256:6bb77b2dcb06b20f9f4b4a8454caa581cd4dd0643a08bacf821216a16d9c8354", size = 6482084, upload-time = "2026-04-01T14:45:47.568Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/13/306d275efd3a3453f72114b7431c877d10b1154014c1ebbedd067770d629/pillow-12.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6562ace0d3fb5f20ed7290f1f929cae41b25ae29528f2af1722966a0a02e2aa1", size = 7225152, upload-time = "2026-04-01T14:45:50.032Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/6e/cf826fae916b8658848d7b9f38d88da6396895c676e8086fc0988073aaf8/pillow-12.2.0-cp314-cp314t-win_arm64.whl", hash = "sha256:aa88ccfe4e32d362816319ed727a004423aab09c5cea43c01a4b435643fa34eb", size = 2556579, upload-time = "2026-04-01T14:45:52.529Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/b7/2437044fb910f499610356d1352e3423753c98e34f915252aafecc64889f/pillow-12.2.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0538bd5e05efec03ae613fd89c4ce0368ecd2ba239cc25b9f9be7ed426b0af1f", size = 5273969, upload-time = "2026-04-01T14:45:55.538Z" },
+ { url = "https://files.pythonhosted.org/packages/f6/f4/8316e31de11b780f4ac08ef3654a75555e624a98db1056ecb2122d008d5a/pillow-12.2.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:394167b21da716608eac917c60aa9b969421b5dcbbe02ae7f013e7b85811c69d", size = 4659674, upload-time = "2026-04-01T14:45:58.093Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/37/664fca7201f8bb2aa1d20e2c3d5564a62e6ae5111741966c8319ca802361/pillow-12.2.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5d04bfa02cc2d23b497d1e90a0f927070043f6cbf303e738300532379a4b4e0f", size = 5288479, upload-time = "2026-04-01T14:46:01.141Z" },
+ { url = "https://files.pythonhosted.org/packages/49/62/5b0ed78fce87346be7a5cfcfaaad91f6a1f98c26f86bdbafa2066c647ef6/pillow-12.2.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0c838a5125cee37e68edec915651521191cef1e6aa336b855f495766e77a366e", size = 7032230, upload-time = "2026-04-01T14:46:03.874Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/28/ec0fc38107fc32536908034e990c47914c57cd7c5a3ece4d8d8f7ffd7e27/pillow-12.2.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a6c9fa44005fa37a91ebfc95d081e8079757d2e904b27103f4f5fa6f0bf78c0", size = 5355404, upload-time = "2026-04-01T14:46:06.33Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/8b/51b0eddcfa2180d60e41f06bd6d0a62202b20b59c68f5a132e615b75aecf/pillow-12.2.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:25373b66e0dd5905ed63fa3cae13c82fbddf3079f2c8bf15c6fb6a35586324c1", size = 6002215, upload-time = "2026-04-01T14:46:08.83Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/60/5382c03e1970de634027cee8e1b7d39776b778b81812aaf45b694dfe9e28/pillow-12.2.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:bfa9c230d2fe991bed5318a5f119bd6780cda2915cca595393649fc118ab895e", size = 7080946, upload-time = "2026-04-01T14:46:11.734Z" },
]
[[package]]
name = "pluggy"
version = "1.6.0"
-source = { registry = "https://pypi.org/simple" }
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
@@ -1223,164 +1340,171 @@ wheels = [
[[package]]
name = "psutil"
-version = "7.1.3"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/e1/88/bdd0a41e5857d5d703287598cbf08dad90aed56774ea52ae071bae9071b6/psutil-7.1.3.tar.gz", hash = "sha256:6c86281738d77335af7aec228328e944b30930899ea760ecf33a4dba66be5e74", size = 489059, upload-time = "2025-11-02T12:25:54.619Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/bd/93/0c49e776b8734fef56ec9c5c57f923922f2cf0497d62e0f419465f28f3d0/psutil-7.1.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0005da714eee687b4b8decd3d6cc7c6db36215c9e74e5ad2264b90c3df7d92dc", size = 239751, upload-time = "2025-11-02T12:25:58.161Z" },
- { url = "https://files.pythonhosted.org/packages/6f/8d/b31e39c769e70780f007969815195a55c81a63efebdd4dbe9e7a113adb2f/psutil-7.1.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:19644c85dcb987e35eeeaefdc3915d059dac7bd1167cdcdbf27e0ce2df0c08c0", size = 240368, upload-time = "2025-11-02T12:26:00.491Z" },
- { url = "https://files.pythonhosted.org/packages/62/61/23fd4acc3c9eebbf6b6c78bcd89e5d020cfde4acf0a9233e9d4e3fa698b4/psutil-7.1.3-cp313-cp313t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:95ef04cf2e5ba0ab9eaafc4a11eaae91b44f4ef5541acd2ee91d9108d00d59a7", size = 287134, upload-time = "2025-11-02T12:26:02.613Z" },
- { url = "https://files.pythonhosted.org/packages/30/1c/f921a009ea9ceb51aa355cb0cc118f68d354db36eae18174bab63affb3e6/psutil-7.1.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1068c303be3a72f8e18e412c5b2a8f6d31750fb152f9cb106b54090296c9d251", size = 289904, upload-time = "2025-11-02T12:26:05.207Z" },
- { url = "https://files.pythonhosted.org/packages/a6/82/62d68066e13e46a5116df187d319d1724b3f437ddd0f958756fc052677f4/psutil-7.1.3-cp313-cp313t-win_amd64.whl", hash = "sha256:18349c5c24b06ac5612c0428ec2a0331c26443d259e2a0144a9b24b4395b58fa", size = 249642, upload-time = "2025-11-02T12:26:07.447Z" },
- { url = "https://files.pythonhosted.org/packages/df/ad/c1cd5fe965c14a0392112f68362cfceb5230819dbb5b1888950d18a11d9f/psutil-7.1.3-cp313-cp313t-win_arm64.whl", hash = "sha256:c525ffa774fe4496282fb0b1187725793de3e7c6b29e41562733cae9ada151ee", size = 245518, upload-time = "2025-11-02T12:26:09.719Z" },
- { url = "https://files.pythonhosted.org/packages/2e/bb/6670bded3e3236eb4287c7bcdc167e9fae6e1e9286e437f7111caed2f909/psutil-7.1.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:b403da1df4d6d43973dc004d19cee3b848e998ae3154cc8097d139b77156c353", size = 239843, upload-time = "2025-11-02T12:26:11.968Z" },
- { url = "https://files.pythonhosted.org/packages/b8/66/853d50e75a38c9a7370ddbeefabdd3d3116b9c31ef94dc92c6729bc36bec/psutil-7.1.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ad81425efc5e75da3f39b3e636293360ad8d0b49bed7df824c79764fb4ba9b8b", size = 240369, upload-time = "2025-11-02T12:26:14.358Z" },
- { url = "https://files.pythonhosted.org/packages/41/bd/313aba97cb5bfb26916dc29cf0646cbe4dd6a89ca69e8c6edce654876d39/psutil-7.1.3-cp314-cp314t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8f33a3702e167783a9213db10ad29650ebf383946e91bc77f28a5eb083496bc9", size = 288210, upload-time = "2025-11-02T12:26:16.699Z" },
- { url = "https://files.pythonhosted.org/packages/c2/fa/76e3c06e760927a0cfb5705eb38164254de34e9bd86db656d4dbaa228b04/psutil-7.1.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fac9cd332c67f4422504297889da5ab7e05fd11e3c4392140f7370f4208ded1f", size = 291182, upload-time = "2025-11-02T12:26:18.848Z" },
- { url = "https://files.pythonhosted.org/packages/0f/1d/5774a91607035ee5078b8fd747686ebec28a962f178712de100d00b78a32/psutil-7.1.3-cp314-cp314t-win_amd64.whl", hash = "sha256:3792983e23b69843aea49c8f5b8f115572c5ab64c153bada5270086a2123c7e7", size = 250466, upload-time = "2025-11-02T12:26:21.183Z" },
- { url = "https://files.pythonhosted.org/packages/00/ca/e426584bacb43a5cb1ac91fae1937f478cd8fbe5e4ff96574e698a2c77cd/psutil-7.1.3-cp314-cp314t-win_arm64.whl", hash = "sha256:31d77fcedb7529f27bb3a0472bea9334349f9a04160e8e6e5020f22c59893264", size = 245756, upload-time = "2025-11-02T12:26:23.148Z" },
- { url = "https://files.pythonhosted.org/packages/ef/94/46b9154a800253e7ecff5aaacdf8ebf43db99de4a2dfa18575b02548654e/psutil-7.1.3-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2bdbcd0e58ca14996a42adf3621a6244f1bb2e2e528886959c72cf1e326677ab", size = 238359, upload-time = "2025-11-02T12:26:25.284Z" },
- { url = "https://files.pythonhosted.org/packages/68/3a/9f93cff5c025029a36d9a92fef47220ab4692ee7f2be0fba9f92813d0cb8/psutil-7.1.3-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:bc31fa00f1fbc3c3802141eede66f3a2d51d89716a194bf2cd6fc68310a19880", size = 239171, upload-time = "2025-11-02T12:26:27.23Z" },
- { url = "https://files.pythonhosted.org/packages/ce/b1/5f49af514f76431ba4eea935b8ad3725cdeb397e9245ab919dbc1d1dc20f/psutil-7.1.3-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3bb428f9f05c1225a558f53e30ccbad9930b11c3fc206836242de1091d3e7dd3", size = 263261, upload-time = "2025-11-02T12:26:29.48Z" },
- { url = "https://files.pythonhosted.org/packages/e0/95/992c8816a74016eb095e73585d747e0a8ea21a061ed3689474fabb29a395/psutil-7.1.3-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:56d974e02ca2c8eb4812c3f76c30e28836fffc311d55d979f1465c1feeb2b68b", size = 264635, upload-time = "2025-11-02T12:26:31.74Z" },
- { url = "https://files.pythonhosted.org/packages/55/4c/c3ed1a622b6ae2fd3c945a366e64eb35247a31e4db16cf5095e269e8eb3c/psutil-7.1.3-cp37-abi3-win_amd64.whl", hash = "sha256:f39c2c19fe824b47484b96f9692932248a54c43799a84282cfe58d05a6449efd", size = 247633, upload-time = "2025-11-02T12:26:33.887Z" },
- { url = "https://files.pythonhosted.org/packages/c9/ad/33b2ccec09bf96c2b2ef3f9a6f66baac8253d7565d8839e024a6b905d45d/psutil-7.1.3-cp37-abi3-win_arm64.whl", hash = "sha256:bd0d69cee829226a761e92f28140bec9a5ee9d5b4fb4b0cc589068dbfff559b1", size = 244608, upload-time = "2025-11-02T12:26:36.136Z" },
+version = "7.2.2"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/aa/c6/d1ddf4abb55e93cebc4f2ed8b5d6dbad109ecb8d63748dd2b20ab5e57ebe/psutil-7.2.2.tar.gz", hash = "sha256:0746f5f8d406af344fd547f1c8daa5f5c33dbc293bb8d6a16d80b4bb88f59372", size = 493740, upload-time = "2026-01-28T18:14:54.428Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/51/08/510cbdb69c25a96f4ae523f733cdc963ae654904e8db864c07585ef99875/psutil-7.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2edccc433cbfa046b980b0df0171cd25bcaeb3a68fe9022db0979e7aa74a826b", size = 130595, upload-time = "2026-01-28T18:14:57.293Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/f5/97baea3fe7a5a9af7436301f85490905379b1c6f2dd51fe3ecf24b4c5fbf/psutil-7.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e78c8603dcd9a04c7364f1a3e670cea95d51ee865e4efb3556a3a63adef958ea", size = 131082, upload-time = "2026-01-28T18:14:59.732Z" },
+ { url = "https://files.pythonhosted.org/packages/37/d6/246513fbf9fa174af531f28412297dd05241d97a75911ac8febefa1a53c6/psutil-7.2.2-cp313-cp313t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1a571f2330c966c62aeda00dd24620425d4b0cc86881c89861fbc04549e5dc63", size = 181476, upload-time = "2026-01-28T18:15:01.884Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/b5/9182c9af3836cca61696dabe4fd1304e17bc56cb62f17439e1154f225dd3/psutil-7.2.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:917e891983ca3c1887b4ef36447b1e0873e70c933afc831c6b6da078ba474312", size = 184062, upload-time = "2026-01-28T18:15:04.436Z" },
+ { url = "https://files.pythonhosted.org/packages/16/ba/0756dca669f5a9300d0cbcbfae9a4c30e446dfc7440ffe43ded5724bfd93/psutil-7.2.2-cp313-cp313t-win_amd64.whl", hash = "sha256:ab486563df44c17f5173621c7b198955bd6b613fb87c71c161f827d3fb149a9b", size = 139893, upload-time = "2026-01-28T18:15:06.378Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/61/8fa0e26f33623b49949346de05ec1ddaad02ed8ba64af45f40a147dbfa97/psutil-7.2.2-cp313-cp313t-win_arm64.whl", hash = "sha256:ae0aefdd8796a7737eccea863f80f81e468a1e4cf14d926bd9b6f5f2d5f90ca9", size = 135589, upload-time = "2026-01-28T18:15:08.03Z" },
+ { url = "https://files.pythonhosted.org/packages/81/69/ef179ab5ca24f32acc1dac0c247fd6a13b501fd5534dbae0e05a1c48b66d/psutil-7.2.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00", size = 130664, upload-time = "2026-01-28T18:15:09.469Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/64/665248b557a236d3fa9efc378d60d95ef56dd0a490c2cd37dafc7660d4a9/psutil-7.2.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7b6d09433a10592ce39b13d7be5a54fbac1d1228ed29abc880fb23df7cb694c9", size = 131087, upload-time = "2026-01-28T18:15:11.724Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/2e/e6782744700d6759ebce3043dcfa661fb61e2fb752b91cdeae9af12c2178/psutil-7.2.2-cp314-cp314t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fa4ecf83bcdf6e6c8f4449aff98eefb5d0604bf88cb883d7da3d8d2d909546a", size = 182383, upload-time = "2026-01-28T18:15:13.445Z" },
+ { url = "https://files.pythonhosted.org/packages/57/49/0a41cefd10cb7505cdc04dab3eacf24c0c2cb158a998b8c7b1d27ee2c1f5/psutil-7.2.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e452c464a02e7dc7822a05d25db4cde564444a67e58539a00f929c51eddda0cf", size = 185210, upload-time = "2026-01-28T18:15:16.002Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/2c/ff9bfb544f283ba5f83ba725a3c5fec6d6b10b8f27ac1dc641c473dc390d/psutil-7.2.2-cp314-cp314t-win_amd64.whl", hash = "sha256:c7663d4e37f13e884d13994247449e9f8f574bc4655d509c3b95e9ec9e2b9dc1", size = 141228, upload-time = "2026-01-28T18:15:18.385Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/fc/f8d9c31db14fcec13748d373e668bc3bed94d9077dbc17fb0eebc073233c/psutil-7.2.2-cp314-cp314t-win_arm64.whl", hash = "sha256:11fe5a4f613759764e79c65cf11ebdf26e33d6dd34336f8a337aa2996d71c841", size = 136284, upload-time = "2026-01-28T18:15:19.912Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/36/5ee6e05c9bd427237b11b3937ad82bb8ad2752d72c6969314590dd0c2f6e/psutil-7.2.2-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486", size = 129090, upload-time = "2026-01-28T18:15:22.168Z" },
+ { url = "https://files.pythonhosted.org/packages/80/c4/f5af4c1ca8c1eeb2e92ccca14ce8effdeec651d5ab6053c589b074eda6e1/psutil-7.2.2-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a7b04c10f32cc88ab39cbf606e117fd74721c831c98a27dc04578deb0c16979", size = 129859, upload-time = "2026-01-28T18:15:23.795Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/70/5d8df3b09e25bce090399cf48e452d25c935ab72dad19406c77f4e828045/psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:076a2d2f923fd4821644f5ba89f059523da90dc9014e85f8e45a5774ca5bc6f9", size = 155560, upload-time = "2026-01-28T18:15:25.976Z" },
+ { url = "https://files.pythonhosted.org/packages/63/65/37648c0c158dc222aba51c089eb3bdfa238e621674dc42d48706e639204f/psutil-7.2.2-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b0726cecd84f9474419d67252add4ac0cd9811b04d61123054b9fb6f57df6e9e", size = 156997, upload-time = "2026-01-28T18:15:27.794Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/13/125093eadae863ce03c6ffdbae9929430d116a246ef69866dad94da3bfbc/psutil-7.2.2-cp36-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8", size = 148972, upload-time = "2026-01-28T18:15:29.342Z" },
+ { url = "https://files.pythonhosted.org/packages/04/78/0acd37ca84ce3ddffaa92ef0f571e073faa6d8ff1f0559ab1272188ea2be/psutil-7.2.2-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b58fabe35e80b264a4e3bb23e6b96f9e45a3df7fb7eed419ac0e5947c61e47cc", size = 148266, upload-time = "2026-01-28T18:15:31.597Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/90/e2159492b5426be0c1fef7acba807a03511f97c5f86b3caeda6ad92351a7/psutil-7.2.2-cp37-abi3-win_amd64.whl", hash = "sha256:eb7e81434c8d223ec4a219b5fc1c47d0417b12be7ea866e24fb5ad6e84b3d988", size = 137737, upload-time = "2026-01-28T18:15:33.849Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/c7/7bb2e321574b10df20cbde462a94e2b71d05f9bbda251ef27d104668306a/psutil-7.2.2-cp37-abi3-win_arm64.whl", hash = "sha256:8c233660f575a5a89e6d4cb65d9f938126312bca76d8fe087b947b3a1aaac9ee", size = 134617, upload-time = "2026-01-28T18:15:36.514Z" },
]
[[package]]
name = "pydantic"
-version = "2.12.4"
-source = { registry = "https://pypi.org/simple" }
+version = "2.13.3"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
dependencies = [
{ name = "annotated-types" },
{ name = "pydantic-core" },
{ name = "typing-extensions" },
{ name = "typing-inspection" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/96/ad/a17bc283d7d81837c061c49e3eaa27a45991759a1b7eae1031921c6bd924/pydantic-2.12.4.tar.gz", hash = "sha256:0f8cb9555000a4b5b617f66bfd2566264c4984b27589d3b845685983e8ea85ac", size = 821038, upload-time = "2025-11-05T10:50:08.59Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/d9/e4/40d09941a2cebcb20609b86a559817d5b9291c49dd6f8c87e5feffbe703a/pydantic-2.13.3.tar.gz", hash = "sha256:af09e9d1d09f4e7fe37145c1f577e1d61ceb9a41924bf0094a36506285d0a84d", size = 844068, upload-time = "2026-04-20T14:46:43.632Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/82/2f/e68750da9b04856e2a7ec56fc6f034a5a79775e9b9a81882252789873798/pydantic-2.12.4-py3-none-any.whl", hash = "sha256:92d3d202a745d46f9be6df459ac5a064fdaa3c1c4cd8adcfa332ccf3c05f871e", size = 463400, upload-time = "2025-11-05T10:50:06.732Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/0a/fd7d723f8f8153418fb40cf9c940e82004fce7e987026b08a68a36dd3fe7/pydantic-2.13.3-py3-none-any.whl", hash = "sha256:6db14ac8dfc9a1e57f87ea2c0de670c251240f43cb0c30a5130e9720dc612927", size = 471981, upload-time = "2026-04-20T14:46:41.402Z" },
]
[[package]]
name = "pydantic-core"
-version = "2.41.5"
-source = { registry = "https://pypi.org/simple" }
+version = "2.46.3"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
dependencies = [
{ name = "typing-extensions" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/e8/72/74a989dd9f2084b3d9530b0915fdda64ac48831c30dbf7c72a41a5232db8/pydantic_core-2.41.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6", size = 2105873, upload-time = "2025-11-04T13:39:31.373Z" },
- { url = "https://files.pythonhosted.org/packages/12/44/37e403fd9455708b3b942949e1d7febc02167662bf1a7da5b78ee1ea2842/pydantic_core-2.41.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b", size = 1899826, upload-time = "2025-11-04T13:39:32.897Z" },
- { url = "https://files.pythonhosted.org/packages/33/7f/1d5cab3ccf44c1935a359d51a8a2a9e1a654b744b5e7f80d41b88d501eec/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a", size = 1917869, upload-time = "2025-11-04T13:39:34.469Z" },
- { url = "https://files.pythonhosted.org/packages/6e/6a/30d94a9674a7fe4f4744052ed6c5e083424510be1e93da5bc47569d11810/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8", size = 2063890, upload-time = "2025-11-04T13:39:36.053Z" },
- { url = "https://files.pythonhosted.org/packages/50/be/76e5d46203fcb2750e542f32e6c371ffa9b8ad17364cf94bb0818dbfb50c/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e", size = 2229740, upload-time = "2025-11-04T13:39:37.753Z" },
- { url = "https://files.pythonhosted.org/packages/d3/ee/fed784df0144793489f87db310a6bbf8118d7b630ed07aa180d6067e653a/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1", size = 2350021, upload-time = "2025-11-04T13:39:40.94Z" },
- { url = "https://files.pythonhosted.org/packages/c8/be/8fed28dd0a180dca19e72c233cbf58efa36df055e5b9d90d64fd1740b828/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b", size = 2066378, upload-time = "2025-11-04T13:39:42.523Z" },
- { url = "https://files.pythonhosted.org/packages/b0/3b/698cf8ae1d536a010e05121b4958b1257f0b5522085e335360e53a6b1c8b/pydantic_core-2.41.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b", size = 2175761, upload-time = "2025-11-04T13:39:44.553Z" },
- { url = "https://files.pythonhosted.org/packages/b8/ba/15d537423939553116dea94ce02f9c31be0fa9d0b806d427e0308ec17145/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284", size = 2146303, upload-time = "2025-11-04T13:39:46.238Z" },
- { url = "https://files.pythonhosted.org/packages/58/7f/0de669bf37d206723795f9c90c82966726a2ab06c336deba4735b55af431/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594", size = 2340355, upload-time = "2025-11-04T13:39:48.002Z" },
- { url = "https://files.pythonhosted.org/packages/e5/de/e7482c435b83d7e3c3ee5ee4451f6e8973cff0eb6007d2872ce6383f6398/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e", size = 2319875, upload-time = "2025-11-04T13:39:49.705Z" },
- { url = "https://files.pythonhosted.org/packages/fe/e6/8c9e81bb6dd7560e33b9053351c29f30c8194b72f2d6932888581f503482/pydantic_core-2.41.5-cp311-cp311-win32.whl", hash = "sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b", size = 1987549, upload-time = "2025-11-04T13:39:51.842Z" },
- { url = "https://files.pythonhosted.org/packages/11/66/f14d1d978ea94d1bc21fc98fcf570f9542fe55bfcc40269d4e1a21c19bf7/pydantic_core-2.41.5-cp311-cp311-win_amd64.whl", hash = "sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe", size = 2011305, upload-time = "2025-11-04T13:39:53.485Z" },
- { url = "https://files.pythonhosted.org/packages/56/d8/0e271434e8efd03186c5386671328154ee349ff0354d83c74f5caaf096ed/pydantic_core-2.41.5-cp311-cp311-win_arm64.whl", hash = "sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f", size = 1972902, upload-time = "2025-11-04T13:39:56.488Z" },
- { url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990, upload-time = "2025-11-04T13:39:58.079Z" },
- { url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003, upload-time = "2025-11-04T13:39:59.956Z" },
- { url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" },
- { url = "https://files.pythonhosted.org/packages/38/de/8c36b5198a29bdaade07b5985e80a233a5ac27137846f3bc2d3b40a47360/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", size = 2052578, upload-time = "2025-11-04T13:40:04.401Z" },
- { url = "https://files.pythonhosted.org/packages/00/b5/0e8e4b5b081eac6cb3dbb7e60a65907549a1ce035a724368c330112adfdd/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", size = 2208504, upload-time = "2025-11-04T13:40:06.072Z" },
- { url = "https://files.pythonhosted.org/packages/77/56/87a61aad59c7c5b9dc8caad5a41a5545cba3810c3e828708b3d7404f6cef/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", size = 2335816, upload-time = "2025-11-04T13:40:07.835Z" },
- { url = "https://files.pythonhosted.org/packages/0d/76/941cc9f73529988688a665a5c0ecff1112b3d95ab48f81db5f7606f522d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", size = 2075366, upload-time = "2025-11-04T13:40:09.804Z" },
- { url = "https://files.pythonhosted.org/packages/d3/43/ebef01f69baa07a482844faaa0a591bad1ef129253ffd0cdaa9d8a7f72d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", size = 2171698, upload-time = "2025-11-04T13:40:12.004Z" },
- { url = "https://files.pythonhosted.org/packages/b1/87/41f3202e4193e3bacfc2c065fab7706ebe81af46a83d3e27605029c1f5a6/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", size = 2132603, upload-time = "2025-11-04T13:40:13.868Z" },
- { url = "https://files.pythonhosted.org/packages/49/7d/4c00df99cb12070b6bccdef4a195255e6020a550d572768d92cc54dba91a/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", size = 2329591, upload-time = "2025-11-04T13:40:15.672Z" },
- { url = "https://files.pythonhosted.org/packages/cc/6a/ebf4b1d65d458f3cda6a7335d141305dfa19bdc61140a884d165a8a1bbc7/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", size = 2319068, upload-time = "2025-11-04T13:40:17.532Z" },
- { url = "https://files.pythonhosted.org/packages/49/3b/774f2b5cd4192d5ab75870ce4381fd89cf218af999515baf07e7206753f0/pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d", size = 1985908, upload-time = "2025-11-04T13:40:19.309Z" },
- { url = "https://files.pythonhosted.org/packages/86/45/00173a033c801cacf67c190fef088789394feaf88a98a7035b0e40d53dc9/pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815", size = 2020145, upload-time = "2025-11-04T13:40:21.548Z" },
- { url = "https://files.pythonhosted.org/packages/f9/22/91fbc821fa6d261b376a3f73809f907cec5ca6025642c463d3488aad22fb/pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3", size = 1976179, upload-time = "2025-11-04T13:40:23.393Z" },
- { url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" },
- { url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" },
- { url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" },
- { url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258, upload-time = "2025-11-04T13:40:33.544Z" },
- { url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917, upload-time = "2025-11-04T13:40:35.479Z" },
- { url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186, upload-time = "2025-11-04T13:40:37.436Z" },
- { url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164, upload-time = "2025-11-04T13:40:40.289Z" },
- { url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146, upload-time = "2025-11-04T13:40:42.809Z" },
- { url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788, upload-time = "2025-11-04T13:40:44.752Z" },
- { url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133, upload-time = "2025-11-04T13:40:46.66Z" },
- { url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852, upload-time = "2025-11-04T13:40:48.575Z" },
- { url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679, upload-time = "2025-11-04T13:40:50.619Z" },
- { url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766, upload-time = "2025-11-04T13:40:52.631Z" },
- { url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005, upload-time = "2025-11-04T13:40:54.734Z" },
- { url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" },
- { url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" },
- { url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" },
- { url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691, upload-time = "2025-11-04T13:41:03.504Z" },
- { url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897, upload-time = "2025-11-04T13:41:05.804Z" },
- { url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302, upload-time = "2025-11-04T13:41:07.809Z" },
- { url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877, upload-time = "2025-11-04T13:41:09.827Z" },
- { url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680, upload-time = "2025-11-04T13:41:12.379Z" },
- { url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960, upload-time = "2025-11-04T13:41:14.627Z" },
- { url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102, upload-time = "2025-11-04T13:41:16.868Z" },
- { url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039, upload-time = "2025-11-04T13:41:18.934Z" },
- { url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126, upload-time = "2025-11-04T13:41:21.418Z" },
- { url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489, upload-time = "2025-11-04T13:41:24.076Z" },
- { url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288, upload-time = "2025-11-04T13:41:26.33Z" },
- { url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255, upload-time = "2025-11-04T13:41:28.569Z" },
- { url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760, upload-time = "2025-11-04T13:41:31.055Z" },
- { url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092, upload-time = "2025-11-04T13:41:33.21Z" },
- { url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385, upload-time = "2025-11-04T13:41:35.508Z" },
- { url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832, upload-time = "2025-11-04T13:41:37.732Z" },
- { url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585, upload-time = "2025-11-04T13:41:40Z" },
- { url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078, upload-time = "2025-11-04T13:41:42.323Z" },
- { url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914, upload-time = "2025-11-04T13:41:45.221Z" },
- { url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560, upload-time = "2025-11-04T13:41:47.474Z" },
- { url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244, upload-time = "2025-11-04T13:41:49.992Z" },
- { url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955, upload-time = "2025-11-04T13:41:54.079Z" },
- { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" },
- { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" },
- { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" },
- { url = "https://files.pythonhosted.org/packages/11/72/90fda5ee3b97e51c494938a4a44c3a35a9c96c19bba12372fb9c634d6f57/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034", size = 2115441, upload-time = "2025-11-04T13:42:39.557Z" },
- { url = "https://files.pythonhosted.org/packages/1f/53/8942f884fa33f50794f119012dc6a1a02ac43a56407adaac20463df8e98f/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c", size = 1930291, upload-time = "2025-11-04T13:42:42.169Z" },
- { url = "https://files.pythonhosted.org/packages/79/c8/ecb9ed9cd942bce09fc888ee960b52654fbdbede4ba6c2d6e0d3b1d8b49c/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2", size = 1948632, upload-time = "2025-11-04T13:42:44.564Z" },
- { url = "https://files.pythonhosted.org/packages/2e/1b/687711069de7efa6af934e74f601e2a4307365e8fdc404703afc453eab26/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad", size = 2138905, upload-time = "2025-11-04T13:42:47.156Z" },
- { url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495, upload-time = "2025-11-04T13:42:49.689Z" },
- { url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388, upload-time = "2025-11-04T13:42:52.215Z" },
- { url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" },
- { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" },
- { url = "https://files.pythonhosted.org/packages/5f/9b/1b3f0e9f9305839d7e84912f9e8bfbd191ed1b1ef48083609f0dabde978c/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26", size = 2101980, upload-time = "2025-11-04T13:43:25.97Z" },
- { url = "https://files.pythonhosted.org/packages/a4/ed/d71fefcb4263df0da6a85b5d8a7508360f2f2e9b3bf5814be9c8bccdccc1/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808", size = 1923865, upload-time = "2025-11-04T13:43:28.763Z" },
- { url = "https://files.pythonhosted.org/packages/ce/3a/626b38db460d675f873e4444b4bb030453bbe7b4ba55df821d026a0493c4/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc", size = 2134256, upload-time = "2025-11-04T13:43:31.71Z" },
- { url = "https://files.pythonhosted.org/packages/83/d9/8412d7f06f616bbc053d30cb4e5f76786af3221462ad5eee1f202021eb4e/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1", size = 2174762, upload-time = "2025-11-04T13:43:34.744Z" },
- { url = "https://files.pythonhosted.org/packages/55/4c/162d906b8e3ba3a99354e20faa1b49a85206c47de97a639510a0e673f5da/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84", size = 2143141, upload-time = "2025-11-04T13:43:37.701Z" },
- { url = "https://files.pythonhosted.org/packages/1f/f2/f11dd73284122713f5f89fc940f370d035fa8e1e078d446b3313955157fe/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770", size = 2330317, upload-time = "2025-11-04T13:43:40.406Z" },
- { url = "https://files.pythonhosted.org/packages/88/9d/b06ca6acfe4abb296110fb1273a4d848a0bfb2ff65f3ee92127b3244e16b/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f", size = 2316992, upload-time = "2025-11-04T13:43:43.602Z" },
- { url = "https://files.pythonhosted.org/packages/36/c7/cfc8e811f061c841d7990b0201912c3556bfeb99cdcb7ed24adc8d6f8704/pydantic_core-2.41.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51", size = 2145302, upload-time = "2025-11-04T13:43:46.64Z" },
+sdist = { url = "https://files.pythonhosted.org/packages/2a/ef/f7abb56c49382a246fd2ce9c799691e3c3e7175ec74b14d99e798bcddb1a/pydantic_core-2.46.3.tar.gz", hash = "sha256:41c178f65b8c29807239d47e6050262eb6bf84eb695e41101e62e38df4a5bc2c", size = 471412, upload-time = "2026-04-20T14:40:56.672Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/22/a2/1ba90a83e85a3f94c796b184f3efde9c72f2830dcda493eea8d59ba78e6d/pydantic_core-2.46.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ab124d49d0459b2373ecf54118a45c28a1e6d4192a533fbc915e70f556feb8e5", size = 2106740, upload-time = "2026-04-20T14:41:20.932Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/f6/99ae893c89a0b9d3daec9f95487aa676709aa83f67643b3f0abaf4ab628a/pydantic_core-2.46.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cca67d52a5c7a16aed2b3999e719c4bcf644074eac304a5d3d62dd70ae7d4b2c", size = 1948293, upload-time = "2026-04-20T14:43:42.115Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/b8/2e8e636dc9e3f16c2e16bf0849e24be82c5ee82c603c65fc0326666328fc/pydantic_core-2.46.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c024e08c0ba23e6fd68c771a521e9d6a792f2ebb0fa734296b36394dc30390e", size = 1973222, upload-time = "2026-04-20T14:41:57.841Z" },
+ { url = "https://files.pythonhosted.org/packages/34/36/0e730beec4d83c5306f417afbd82ff237d9a21e83c5edf675f31ed84c1fe/pydantic_core-2.46.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6645ce7eec4928e29a1e3b3d5c946621d105d3e79f0c9cddf07c2a9770949287", size = 2053852, upload-time = "2026-04-20T14:40:43.077Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/f0/3071131f47e39136a17814576e0fada9168569f7f8c0e6ac4d1ede6a4958/pydantic_core-2.46.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a712c7118e6c5ea96562f7b488435172abb94a3c53c22c9efc1412264a45cbbe", size = 2221134, upload-time = "2026-04-20T14:43:03.349Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/a9/a2dc023eec5aa4b02a467874bad32e2446957d2adcab14e107eab502e978/pydantic_core-2.46.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:69a868ef3ff206343579021c40faf3b1edc64b1cc508ff243a28b0a514ccb050", size = 2279785, upload-time = "2026-04-20T14:41:19.285Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/44/93f489d16fb63fbd41c670441536541f6e8cfa1e5a69f40bc9c5d30d8c90/pydantic_core-2.46.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc7e8c32db809aa0f6ea1d6869ebc8518a65d5150fdfad8bcae6a49ae32a22e2", size = 2089404, upload-time = "2026-04-20T14:43:10.108Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/78/8692e3aa72b2d004f7a5d937f1dfdc8552ba26caf0bec75f342c40f00dec/pydantic_core-2.46.3-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:3481bd1341dc85779ee506bc8e1196a277ace359d89d28588a9468c3ecbe63fa", size = 2114898, upload-time = "2026-04-20T14:44:51.475Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/62/e83133f2e7832532060175cebf1f13748f4c7e7e7165cdd1f611f174494b/pydantic_core-2.46.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8690eba565c6d68ffd3a8655525cbdd5246510b44a637ee2c6c03a7ebfe64d3c", size = 2157856, upload-time = "2026-04-20T14:43:46.64Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/ec/6a500e3ad7718ee50583fae79c8651f5d37e3abce1fa9ae177ae65842c53/pydantic_core-2.46.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4de88889d7e88d50d40ee5b39d5dac0bcaef9ba91f7e536ac064e6b2834ecccf", size = 2180168, upload-time = "2026-04-20T14:42:00.302Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/53/8267811054b1aa7fc1dc7ded93812372ef79a839f5e23558136a6afbfde1/pydantic_core-2.46.3-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:e480080975c1ef7f780b8f99ed72337e7cc5efea2e518a20a692e8e7b278eb8b", size = 2322885, upload-time = "2026-04-20T14:41:05.253Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/c1/1c0acdb3aa0856ddc4ecc55214578f896f2de16f400cf51627eb3c26c1c4/pydantic_core-2.46.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:de3a5c376f8cd94da9a1b8fd3dd1c16c7a7b216ed31dc8ce9fd7a22bf13b836e", size = 2360328, upload-time = "2026-04-20T14:41:43.991Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/d0/ef39cd0f4a926814f360e71c1adeab48ad214d9727e4deb48eedfb5bce1a/pydantic_core-2.46.3-cp311-cp311-win32.whl", hash = "sha256:fc331a5314ffddd5385b9ee9d0d2fee0b13c27e0e02dad71b1ae5d6561f51eeb", size = 1979464, upload-time = "2026-04-20T14:43:12.215Z" },
+ { url = "https://files.pythonhosted.org/packages/18/9c/f41951b0d858e343f1cf09398b2a7b3014013799744f2c4a8ad6a3eec4f2/pydantic_core-2.46.3-cp311-cp311-win_amd64.whl", hash = "sha256:b5b9c6cf08a8a5e502698f5e153056d12c34b8fb30317e0c5fd06f45162a6346", size = 2070837, upload-time = "2026-04-20T14:41:47.707Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/1e/264a17cd582f6ed50950d4d03dd5fefd84e570e238afe1cb3e25cf238769/pydantic_core-2.46.3-cp311-cp311-win_arm64.whl", hash = "sha256:5dfd51cf457482f04ec49491811a2b8fd5b843b64b11eecd2d7a1ee596ea78a6", size = 2053647, upload-time = "2026-04-20T14:42:27.535Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/cb/5b47425556ecc1f3fe18ed2a0083188aa46e1dd812b06e406475b3a5d536/pydantic_core-2.46.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:b11b59b3eee90a80a36701ddb4576d9ae31f93f05cb9e277ceaa09e6bf074a67", size = 2101946, upload-time = "2026-04-20T14:40:52.581Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/4f/2fb62c2267cae99b815bbf4a7b9283812c88ca3153ef29f7707200f1d4e5/pydantic_core-2.46.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:af8653713055ea18a3abc1537fe2ebc42f5b0bbb768d1eb79fd74eb47c0ac089", size = 1951612, upload-time = "2026-04-20T14:42:42.996Z" },
+ { url = "https://files.pythonhosted.org/packages/50/6e/b7348fd30d6556d132cddd5bd79f37f96f2601fe0608afac4f5fb01ec0b3/pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:75a519dab6d63c514f3a81053e5266c549679e4aa88f6ec57f2b7b854aceb1b0", size = 1977027, upload-time = "2026-04-20T14:42:02.001Z" },
+ { url = "https://files.pythonhosted.org/packages/82/11/31d60ee2b45540d3fb0b29302a393dbc01cd771c473f5b5147bcd353e593/pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a6cd87cb1575b1ad05ba98894c5b5c96411ef678fa2f6ed2576607095b8d9789", size = 2063008, upload-time = "2026-04-20T14:44:17.952Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/db/3a9d1957181b59258f44a2300ab0f0be9d1e12d662a4f57bb31250455c52/pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f80a55484b8d843c8ada81ebf70a682f3f00a3d40e378c06cf17ecb44d280d7d", size = 2233082, upload-time = "2026-04-20T14:40:57.934Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/e1/3277c38792aeb5cfb18c2f0c5785a221d9ff4e149abbe1184d53d5f72273/pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3861f1731b90c50a3266316b9044f5c9b405eecb8e299b0a7120596334e4fe9c", size = 2304615, upload-time = "2026-04-20T14:42:12.584Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/d5/e3d9717c9eba10855325650afd2a9cba8e607321697f18953af9d562da2f/pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb528e295ed31570ac3dcc9bfdd6e0150bc11ce6168ac87a8082055cf1a67395", size = 2094380, upload-time = "2026-04-20T14:43:05.522Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/20/abac35dedcbfd66c6f0b03e4e3564511771d6c9b7ede10a362d03e110d9b/pydantic_core-2.46.3-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:367508faa4973b992b271ba1494acaab36eb7e8739d1e47be5035fb1ea225396", size = 2135429, upload-time = "2026-04-20T14:41:55.549Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/a5/41bfd1df69afad71b5cf0535055bccc73022715ad362edbc124bc1e021d7/pydantic_core-2.46.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5ad3c826fe523e4becf4fe39baa44286cff85ef137c729a2c5e269afbfd0905d", size = 2174582, upload-time = "2026-04-20T14:41:45.96Z" },
+ { url = "https://files.pythonhosted.org/packages/79/65/38d86ea056b29b2b10734eb23329b7a7672ca604df4f2b6e9c02d4ee22fe/pydantic_core-2.46.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ec638c5d194ef8af27db69f16c954a09797c0dc25015ad6123eb2c73a4d271ca", size = 2187533, upload-time = "2026-04-20T14:40:55.367Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/55/a1129141678a2026badc539ad1dee0a71d06f54c2f06a4bd68c030ac781b/pydantic_core-2.46.3-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:28ed528c45446062ee66edb1d33df5d88828ae167de76e773a3c7f64bd14e976", size = 2332985, upload-time = "2026-04-20T14:44:13.05Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/60/cb26f4077719f709e54819f4e8e1d43f4091f94e285eb6bd21e1190a7b7c/pydantic_core-2.46.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aed19d0c783886d5bd86d80ae5030006b45e28464218747dcf83dabfdd092c7b", size = 2373670, upload-time = "2026-04-20T14:41:53.421Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/7e/c3f21882bdf1d8d086876f81b5e296206c69c6082551d776895de7801fa0/pydantic_core-2.46.3-cp312-cp312-win32.whl", hash = "sha256:06d5d8820cbbdb4147578c1fe7ffcd5b83f34508cb9f9ab76e807be7db6ff0a4", size = 1966722, upload-time = "2026-04-20T14:44:30.588Z" },
+ { url = "https://files.pythonhosted.org/packages/57/be/6b5e757b859013ebfbd7adba02f23b428f37c86dcbf78b5bb0b4ffd36e99/pydantic_core-2.46.3-cp312-cp312-win_amd64.whl", hash = "sha256:c3212fda0ee959c1dd04c60b601ec31097aaa893573a3a1abd0a47bcac2968c1", size = 2072970, upload-time = "2026-04-20T14:42:54.248Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/f8/a989b21cc75e9a32d24192ef700eea606521221a89faa40c919ce884f2b1/pydantic_core-2.46.3-cp312-cp312-win_arm64.whl", hash = "sha256:f1f8338dd7a7f31761f1f1a3c47503a9a3b34eea3c8b01fa6ee96408affb5e72", size = 2035963, upload-time = "2026-04-20T14:44:20.4Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/3c/9b5e8eb9821936d065439c3b0fb1490ffa64163bfe7e1595985a47896073/pydantic_core-2.46.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:12bc98de041458b80c86c56b24df1d23832f3e166cbaff011f25d187f5c62c37", size = 2102109, upload-time = "2026-04-20T14:41:24.219Z" },
+ { url = "https://files.pythonhosted.org/packages/91/97/1c41d1f5a19f241d8069f1e249853bcce378cdb76eec8ab636d7bc426280/pydantic_core-2.46.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:85348b8f89d2c3508b65b16c3c33a4da22b8215138d8b996912bb1532868885f", size = 1951820, upload-time = "2026-04-20T14:42:14.236Z" },
+ { url = "https://files.pythonhosted.org/packages/30/b4/d03a7ae14571bc2b6b3c7b122441154720619afe9a336fa3a95434df5e2f/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1105677a6df914b1fb71a81b96c8cce7726857e1717d86001f29be06a25ee6f8", size = 1977785, upload-time = "2026-04-20T14:42:31.648Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/0c/4086f808834b59e3c8f1aa26df8f4b6d998cdcf354a143d18ef41529d1fe/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:87082cd65669a33adeba5470769e9704c7cf026cc30afb9cc77fd865578ebaad", size = 2062761, upload-time = "2026-04-20T14:40:37.093Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/71/a649be5a5064c2df0db06e0a512c2281134ed2fcc981f52a657936a7527c/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:60e5f66e12c4f5212d08522963380eaaeac5ebd795826cfd19b2dfb0c7a52b9c", size = 2232989, upload-time = "2026-04-20T14:42:59.254Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/84/7756e75763e810b3a710f4724441d1ecc5883b94aacb07ca71c5fb5cfb69/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b6cdf19bf84128d5e7c37e8a73a0c5c10d51103a650ac585d42dd6ae233f2b7f", size = 2303975, upload-time = "2026-04-20T14:41:32.287Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/35/68a762e0c1e31f35fa0dac733cbd9f5b118042853698de9509c8e5bf128b/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:031bb17f4885a43773c8c763089499f242aee2ea85cf17154168775dccdecf35", size = 2095325, upload-time = "2026-04-20T14:42:47.685Z" },
+ { url = "https://files.pythonhosted.org/packages/77/bf/1bf8c9a8e91836c926eae5e3e51dce009bf495a60ca56060689d3df3f340/pydantic_core-2.46.3-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:bcf2a8b2982a6673693eae7348ef3d8cf3979c1d63b54fca7c397a635cc68687", size = 2133368, upload-time = "2026-04-20T14:41:22.766Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/50/87d818d6bab915984995157ceb2380f5aac4e563dddbed6b56f0ed057aba/pydantic_core-2.46.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28e8cf2f52d72ced402a137145923a762cbb5081e48b34312f7a0c8f55928ec3", size = 2173908, upload-time = "2026-04-20T14:42:52.044Z" },
+ { url = "https://files.pythonhosted.org/packages/91/88/a311fb306d0bd6185db41fa14ae888fb81d0baf648a761ae760d30819d33/pydantic_core-2.46.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:17eaface65d9fc5abb940003020309c1bf7a211f5f608d7870297c367e6f9022", size = 2186422, upload-time = "2026-04-20T14:43:29.55Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/79/28fd0d81508525ab2054fef7c77a638c8b5b0afcbbaeee493cf7c3fef7e1/pydantic_core-2.46.3-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:93fd339f23408a07e98950a89644f92c54d8729719a40b30c0a30bb9ebc55d23", size = 2332709, upload-time = "2026-04-20T14:42:16.134Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/21/795bf5fe5c0f379308b8ef19c50dedab2e7711dbc8d0c2acf08f1c7daa05/pydantic_core-2.46.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:23cbdb3aaa74dfe0837975dbf69b469753bbde8eacace524519ffdb6b6e89eb7", size = 2372428, upload-time = "2026-04-20T14:41:10.974Z" },
+ { url = "https://files.pythonhosted.org/packages/45/b3/ed14c659cbe7605e3ef063077680a64680aec81eb1a04763a05190d49b7f/pydantic_core-2.46.3-cp313-cp313-win32.whl", hash = "sha256:610eda2e3838f401105e6326ca304f5da1e15393ae25dacae5c5c63f2c275b13", size = 1965601, upload-time = "2026-04-20T14:41:42.128Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/bb/adb70d9a762ddd002d723fbf1bd492244d37da41e3af7b74ad212609027e/pydantic_core-2.46.3-cp313-cp313-win_amd64.whl", hash = "sha256:68cc7866ed863db34351294187f9b729964c371ba33e31c26f478471c52e1ed0", size = 2071517, upload-time = "2026-04-20T14:43:36.096Z" },
+ { url = "https://files.pythonhosted.org/packages/52/eb/66faefabebfe68bd7788339c9c9127231e680b11906368c67ce112fdb47f/pydantic_core-2.46.3-cp313-cp313-win_arm64.whl", hash = "sha256:f64b5537ac62b231572879cd08ec05600308636a5d63bcbdb15063a466977bec", size = 2035802, upload-time = "2026-04-20T14:43:38.507Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/db/a7bcb4940183fda36022cd18ba8dd12f2dff40740ec7b58ce7457befa416/pydantic_core-2.46.3-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:afa3aa644f74e290cdede48a7b0bee37d1c35e71b05105f6b340d484af536d9b", size = 2097614, upload-time = "2026-04-20T14:44:38.374Z" },
+ { url = "https://files.pythonhosted.org/packages/24/35/e4066358a22e3e99519db370494c7528f5a2aa1367370e80e27e20283543/pydantic_core-2.46.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ced3310e51aa425f7f77da8bbbb5212616655bedbe82c70944320bc1dbe5e018", size = 1951896, upload-time = "2026-04-20T14:40:53.996Z" },
+ { url = "https://files.pythonhosted.org/packages/87/92/37cf4049d1636996e4b888c05a501f40a43ff218983a551d57f9d5e14f0d/pydantic_core-2.46.3-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e29908922ce9da1a30b4da490bd1d3d82c01dcfdf864d2a74aacee674d0bfa34", size = 1979314, upload-time = "2026-04-20T14:41:49.446Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/36/9ff4d676dfbdfb2d591cf43f3d90ded01e15b1404fd101180ed2d62a2fd3/pydantic_core-2.46.3-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0c9ff69140423eea8ed2d5477df3ba037f671f5e897d206d921bc9fdc39613e7", size = 2056133, upload-time = "2026-04-20T14:42:23.574Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/f0/405b442a4d7ba855b06eec8b2bf9c617d43b8432d099dfdc7bf999293495/pydantic_core-2.46.3-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b675ab0a0d5b1c8fdb81195dc5bcefea3f3c240871cdd7ff9a2de8aa50772eb2", size = 2228726, upload-time = "2026-04-20T14:44:22.816Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/f8/65cd92dd5a0bd89ba277a98ecbfaf6fc36bbd3300973c7a4b826d6ab1391/pydantic_core-2.46.3-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0087084960f209a9a4af50ecd1fb063d9ad3658c07bb81a7a53f452dacbfb2ba", size = 2301214, upload-time = "2026-04-20T14:44:48.792Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/86/ef96a4c6e79e7a2d0410826a68fbc0eccc0fd44aa733be199d5fcac3bb87/pydantic_core-2.46.3-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed42e6cc8e1b0e2b9b96e2276bad70ae625d10d6d524aed0c93de974ae029f9f", size = 2099927, upload-time = "2026-04-20T14:41:40.196Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/53/269caf30e0096e0a8a8f929d1982a27b3879872cca2d917d17c2f9fdf4fe/pydantic_core-2.46.3-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:f1771ce258afb3e4201e67d154edbbae712a76a6081079fe247c2f53c6322c22", size = 2128789, upload-time = "2026-04-20T14:41:15.868Z" },
+ { url = "https://files.pythonhosted.org/packages/00/b0/1a6d9b6a587e118482910c244a1c5acf4d192604174132efd12bf0ac486f/pydantic_core-2.46.3-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a7610b6a5242a6c736d8ad47fd5fff87fcfe8f833b281b1c409c3d6835d9227f", size = 2173815, upload-time = "2026-04-20T14:44:25.152Z" },
+ { url = "https://files.pythonhosted.org/packages/87/56/e7e00d4041a7e62b5a40815590114db3b535bf3ca0bf4dca9f16cef25246/pydantic_core-2.46.3-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:ff5e7783bcc5476e1db448bf268f11cb257b1c276d3e89f00b5727be86dd0127", size = 2181608, upload-time = "2026-04-20T14:41:28.933Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/22/4bd23c3d41f7c185d60808a1de83c76cf5aeabf792f6c636a55c3b1ec7f9/pydantic_core-2.46.3-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:9d2e32edcc143bc01e95300671915d9ca052d4f745aa0a49c48d4803f8a85f2c", size = 2326968, upload-time = "2026-04-20T14:42:03.962Z" },
+ { url = "https://files.pythonhosted.org/packages/24/ac/66cd45129e3915e5ade3b292cb3bc7fd537f58f8f8dbdaba6170f7cabb74/pydantic_core-2.46.3-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:6e42d83d1c6b87fa56b521479cff237e626a292f3b31b6345c15a99121b454c1", size = 2369842, upload-time = "2026-04-20T14:41:35.52Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/51/dd4248abb84113615473aa20d5545b7c4cd73c8644003b5259686f93996c/pydantic_core-2.46.3-cp314-cp314-win32.whl", hash = "sha256:07bc6d2a28c3adb4f7c6ae46aa4f2d2929af127f587ed44057af50bf1ce0f505", size = 1959661, upload-time = "2026-04-20T14:41:00.042Z" },
+ { url = "https://files.pythonhosted.org/packages/20/eb/59980e5f1ae54a3b86372bd9f0fa373ea2d402e8cdcd3459334430f91e91/pydantic_core-2.46.3-cp314-cp314-win_amd64.whl", hash = "sha256:8940562319bc621da30714617e6a7eaa6b98c84e8c685bcdc02d7ed5e7c7c44e", size = 2071686, upload-time = "2026-04-20T14:43:16.471Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/db/1cf77e5247047dfee34bc01fa9bca134854f528c8eb053e144298893d370/pydantic_core-2.46.3-cp314-cp314-win_arm64.whl", hash = "sha256:5dcbbcf4d22210ced8f837c96db941bdb078f419543472aca5d9a0bb7cddc7df", size = 2026907, upload-time = "2026-04-20T14:43:31.732Z" },
+ { url = "https://files.pythonhosted.org/packages/57/c0/b3df9f6a543276eadba0a48487b082ca1f201745329d97dbfa287034a230/pydantic_core-2.46.3-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:d0fe3dce1e836e418f912c1ad91c73357d03e556a4d286f441bf34fed2dbeecf", size = 2095047, upload-time = "2026-04-20T14:42:37.982Z" },
+ { url = "https://files.pythonhosted.org/packages/66/57/886a938073b97556c168fd99e1a7305bb363cd30a6d2c76086bf0587b32a/pydantic_core-2.46.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9ce92e58abc722dac1bf835a6798a60b294e48eb0e625ec9fd994b932ac5feee", size = 1934329, upload-time = "2026-04-20T14:43:49.655Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/7c/b42eaa5c34b13b07ecb51da21761297a9b8eb43044c864a035999998f328/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a03e6467f0f5ab796a486146d1b887b2dc5e5f9b3288898c1b1c3ad974e53e4a", size = 1974847, upload-time = "2026-04-20T14:42:10.737Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/9b/92b42db6543e7de4f99ae977101a2967b63122d4b6cf7773812da2d7d5b5/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2798b6ba041b9d70acfb9071a2ea13c8456dd1e6a5555798e41ba7b0790e329c", size = 2041742, upload-time = "2026-04-20T14:40:44.262Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/19/46fbe1efabb5aa2834b43b9454e70f9a83ad9c338c1291e48bdc4fecf167/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9be3e221bdc6d69abf294dcf7aff6af19c31a5cdcc8f0aa3b14be29df4bd03b1", size = 2236235, upload-time = "2026-04-20T14:41:27.307Z" },
+ { url = "https://files.pythonhosted.org/packages/77/da/b3f95bc009ad60ec53120f5d16c6faa8cabdbe8a20d83849a1f2b8728148/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f13936129ce841f2a5ddf6f126fea3c43cd128807b5a59588c37cf10178c2e64", size = 2282633, upload-time = "2026-04-20T14:44:33.271Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/6e/401336117722e28f32fb8220df676769d28ebdf08f2f4469646d404c43a3/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28b5f2ef03416facccb1c6ef744c69793175fd27e44ef15669201601cf423acb", size = 2109679, upload-time = "2026-04-20T14:44:41.065Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/53/b289f9bc8756a32fe718c46f55afaeaf8d489ee18d1a1e7be1db73f42cc4/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:830d1247d77ad23852314f069e9d7ddafeec5f684baf9d7e7065ed46a049c4e6", size = 2108342, upload-time = "2026-04-20T14:42:50.144Z" },
+ { url = "https://files.pythonhosted.org/packages/10/5b/8292fc7c1f9111f1b2b7c1b0dcf1179edcd014fc3ea4517499f50b829d71/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0793c90c1a3c74966e7975eaef3ed30ebdff3260a0f815a62a22adc17e4c01c", size = 2157208, upload-time = "2026-04-20T14:42:08.133Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/9e/f80044e9ec07580f057a89fc131f78dda7a58751ddf52bbe05eaf31db50f/pydantic_core-2.46.3-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:d2d0aead851b66f5245ec0c4fb2612ef457f8bbafefdf65a2bf9d6bac6140f47", size = 2167237, upload-time = "2026-04-20T14:42:25.412Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/84/6781a1b037f3b96be9227edbd1101f6d3946746056231bf4ac48cdff1a8d/pydantic_core-2.46.3-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:2f40e4246676beb31c5ce77c38a55ca4e465c6b38d11ea1bd935420568e0b1ab", size = 2312540, upload-time = "2026-04-20T14:40:40.313Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/db/19c0839feeb728e7df03255581f198dfdf1c2aeb1e174a8420b63c5252e5/pydantic_core-2.46.3-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:cf489cf8986c543939aeee17a09c04d6ffb43bfef8ca16fcbcc5cfdcbed24dba", size = 2369556, upload-time = "2026-04-20T14:41:09.427Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/15/3228774cb7cd45f5f721ddf1b2242747f4eb834d0c491f0c02d606f09fed/pydantic_core-2.46.3-cp314-cp314t-win32.whl", hash = "sha256:ffe0883b56cfc05798bf994164d2b2ff03efe2d22022a2bb080f3b626176dd56", size = 1949756, upload-time = "2026-04-20T14:41:25.717Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/2a/c79cf53fd91e5a87e30d481809f52f9a60dd221e39de66455cf04deaad37/pydantic_core-2.46.3-cp314-cp314t-win_amd64.whl", hash = "sha256:706d9d0ce9cf4593d07270d8e9f53b161f90c57d315aeec4fb4fd7a8b10240d8", size = 2051305, upload-time = "2026-04-20T14:43:18.627Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/db/d8182a7f1d9343a032265aae186eb063fe26ca4c40f256b21e8da4498e89/pydantic_core-2.46.3-cp314-cp314t-win_arm64.whl", hash = "sha256:77706aeb41df6a76568434701e0917da10692da28cb69d5fb6919ce5fdb07374", size = 2026310, upload-time = "2026-04-20T14:41:01.778Z" },
+ { url = "https://files.pythonhosted.org/packages/66/7f/03dbad45cd3aa9083fbc93c210ae8b005af67e4136a14186950a747c6874/pydantic_core-2.46.3-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:9715525891ed524a0a1eb6d053c74d4d4ad5017677fb00af0b7c2644a31bae46", size = 2105683, upload-time = "2026-04-20T14:42:19.779Z" },
+ { url = "https://files.pythonhosted.org/packages/26/22/4dc186ac8ea6b257e9855031f51b62a9637beac4d68ac06bee02f046f836/pydantic_core-2.46.3-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:9d2f400712a99a013aff420ef1eb9be077f8189a36c1e3ef87660b4e1088a874", size = 1940052, upload-time = "2026-04-20T14:43:59.274Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/ca/d376391a5aff1f2e8188960d7873543608130a870961c2b6b5236627c116/pydantic_core-2.46.3-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd2aab0e2e9dc2daf36bd2686c982535d5e7b1d930a1344a7bb6e82baab42a76", size = 1988172, upload-time = "2026-04-20T14:41:17.469Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/6b/523b9f85c23788755d6ab949329de692a2e3a584bc6beb67fef5e035aa9d/pydantic_core-2.46.3-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e9d76736da5f362fabfeea6a69b13b7f2be405c6d6966f06b2f6bfff7e64531", size = 2128596, upload-time = "2026-04-20T14:40:41.707Z" },
+ { url = "https://files.pythonhosted.org/packages/34/42/f426db557e8ab2791bc7562052299944a118655496fbff99914e564c0a94/pydantic_core-2.46.3-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:b12dd51f1187c2eb489af8e20f880362db98e954b54ab792fa5d92e8bcc6b803", size = 2091877, upload-time = "2026-04-20T14:43:27.091Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/4f/86a832a9d14df58e663bfdf4627dc00d3317c2bd583c4fb23390b0f04b8e/pydantic_core-2.46.3-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:f00a0961b125f1a47af7bcc17f00782e12f4cd056f83416006b30111d941dfa3", size = 1932428, upload-time = "2026-04-20T14:40:45.781Z" },
+ { url = "https://files.pythonhosted.org/packages/11/1a/fe857968954d93fb78e0d4b6df5c988c74c4aaa67181c60be7cfe327c0ca/pydantic_core-2.46.3-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57697d7c056aca4bbb680200f96563e841a6386ac1129370a0102592f4dddff5", size = 1997550, upload-time = "2026-04-20T14:44:02.425Z" },
+ { url = "https://files.pythonhosted.org/packages/17/eb/9d89ad2d9b0ba8cd65393d434471621b98912abb10fbe1df08e480ba57b5/pydantic_core-2.46.3-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd35aa21299def8db7ef4fe5c4ff862941a9a158ca7b63d61e66fe67d30416b4", size = 2137657, upload-time = "2026-04-20T14:42:45.149Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/da/99d40830684f81dec901cac521b5b91c095394cc1084b9433393cde1c2df/pydantic_core-2.46.3-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:13afdd885f3d71280cf286b13b310ee0f7ccfefd1dbbb661514a474b726e2f25", size = 2107973, upload-time = "2026-04-20T14:42:06.175Z" },
+ { url = "https://files.pythonhosted.org/packages/99/a5/87024121818d75bbb2a98ddbaf638e40e7a18b5e0f5492c9ca4b1b316107/pydantic_core-2.46.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:f91c0aff3e3ee0928edd1232c57f643a7a003e6edf1860bc3afcdc749cb513f3", size = 1947191, upload-time = "2026-04-20T14:43:14.319Z" },
+ { url = "https://files.pythonhosted.org/packages/60/62/0c1acfe10945b83a6a59d19fbaa92f48825381509e5701b855c08f13db76/pydantic_core-2.46.3-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6529d1d128321a58d30afcc97b49e98836542f68dd41b33c2e972bb9e5290536", size = 2123791, upload-time = "2026-04-20T14:43:22.766Z" },
+ { url = "https://files.pythonhosted.org/packages/75/3e/3b2393b4c8f44285561dc30b00cf307a56a2eff7c483a824db3b8221ca51/pydantic_core-2.46.3-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:975c267cff4f7e7272eacbe50f6cc03ca9a3da4c4fbd66fffd89c94c1e311aa1", size = 2153197, upload-time = "2026-04-20T14:44:27.932Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/75/5af02fb35505051eee727c061f2881c555ab4f8ddb2d42da715a42c9731b/pydantic_core-2.46.3-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:2b8e4f2bbdf71415c544b4b1138b8060db7b6611bc927e8064c769f64bed651c", size = 2181073, upload-time = "2026-04-20T14:43:20.729Z" },
+ { url = "https://files.pythonhosted.org/packages/10/92/7e0e1bd9ca3c68305db037560ca2876f89b2647deb2f8b6319005de37505/pydantic_core-2.46.3-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:e61ea8e9fff9606d09178f577ff8ccdd7206ff73d6552bcec18e1033c4254b85", size = 2315886, upload-time = "2026-04-20T14:44:04.826Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/d8/101655f27eaf3e44558ead736b2795d12500598beed4683f279396fa186e/pydantic_core-2.46.3-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b504bda01bafc69b6d3c7a0c7f039dcf60f47fab70e06fe23f57b5c75bdc82b8", size = 2360528, upload-time = "2026-04-20T14:40:47.431Z" },
+ { url = "https://files.pythonhosted.org/packages/07/0f/1c34a74c8d07136f0d729ffe5e1fdab04fbdaa7684f61a92f92511a84a15/pydantic_core-2.46.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:b00b76f7142fc60c762ce579bd29c8fa44aaa56592dd3c54fab3928d0d4ca6ff", size = 2184144, upload-time = "2026-04-20T14:42:57Z" },
]
[[package]]
name = "pygments"
-version = "2.19.2"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" }
+version = "2.20.0"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" },
]
[[package]]
name = "pyparsing"
-version = "3.2.5"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/f2/a5/181488fc2b9d093e3972d2a472855aae8a03f000592dbfce716a512b3359/pyparsing-3.2.5.tar.gz", hash = "sha256:2df8d5b7b2802ef88e8d016a2eb9c7aeaa923529cd251ed0fe4608275d4105b6", size = 1099274, upload-time = "2025-09-21T04:11:06.277Z" }
+version = "3.3.2"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f3/91/9c6ee907786a473bf81c5f53cf703ba0957b23ab84c264080fb5a450416f/pyparsing-3.3.2.tar.gz", hash = "sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc", size = 6851574, upload-time = "2026-01-21T03:57:59.36Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/10/5e/1aa9a93198c6b64513c9d7752de7422c06402de6600a8767da1524f9570b/pyparsing-3.2.5-py3-none-any.whl", hash = "sha256:e38a4f02064cf41fe6593d328d0512495ad1f3d8a91c4f73fc401b3079a59a5e", size = 113890, upload-time = "2025-09-21T04:11:04.117Z" },
+ { url = "https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d", size = 122781, upload-time = "2026-01-21T03:57:55.912Z" },
]
[[package]]
name = "pytest"
-version = "8.4.2"
-source = { registry = "https://pypi.org/simple" }
+version = "9.0.3"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
{ name = "iniconfig" },
@@ -1388,15 +1512,15 @@ dependencies = [
{ name = "pluggy" },
{ name = "pygments" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165, upload-time = "2026-04-07T17:16:18.027Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" },
]
[[package]]
name = "python-dateutil"
version = "2.9.0.post0"
-source = { registry = "https://pypi.org/simple" }
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
dependencies = [
{ name = "six" },
]
@@ -1407,42 +1531,42 @@ wheels = [
[[package]]
name = "pytz"
-version = "2025.2"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", size = 320884, upload-time = "2025-03-25T02:25:00.538Z" }
+version = "2026.1.post1"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/56/db/b8721d71d945e6a8ac63c0fc900b2067181dbb50805958d4d4661cf7d277/pytz-2026.1.post1.tar.gz", hash = "sha256:3378dde6a0c3d26719182142c56e60c7f9af7e968076f31aae569d72a0358ee1", size = 321088, upload-time = "2026-03-03T07:47:50.683Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225, upload-time = "2025-03-25T02:24:58.468Z" },
+ { url = "https://files.pythonhosted.org/packages/10/99/781fe0c827be2742bcc775efefccb3b048a3a9c6ce9aec0cbf4a101677e5/pytz-2026.1.post1-py2.py3-none-any.whl", hash = "sha256:f2fd16142fda348286a75e1a524be810bb05d444e5a081f37f7affc635035f7a", size = 510489, upload-time = "2026-03-03T07:47:49.167Z" },
]
[[package]]
name = "rich"
-version = "14.2.0"
-source = { registry = "https://pypi.org/simple" }
+version = "15.0.0"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
dependencies = [
{ name = "markdown-it-py" },
{ name = "pygments" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/fb/d2/8920e102050a0de7bfabeb4c4614a49248cf8d5d7a8d01885fbb24dc767a/rich-14.2.0.tar.gz", hash = "sha256:73ff50c7c0c1c77c8243079283f4edb376f0f6442433aecb8ce7e6d0b92d1fe4", size = 219990, upload-time = "2025-10-09T14:16:53.064Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/c0/8f/0722ca900cc807c13a6a0c696dacf35430f72e0ec571c4275d2371fca3e9/rich-15.0.0.tar.gz", hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36", size = 230680, upload-time = "2026-04-12T08:24:00.75Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/25/7a/b0178788f8dc6cafce37a212c99565fa1fe7872c70c6c9c1e1a372d9d88f/rich-14.2.0-py3-none-any.whl", hash = "sha256:76bc51fe2e57d2b1be1f96c524b890b816e334ab4c1e45888799bfaab0021edd", size = 243393, upload-time = "2025-10-09T14:16:51.245Z" },
+ { url = "https://files.pythonhosted.org/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb", size = 310654, upload-time = "2026-04-12T08:24:02.83Z" },
]
[[package]]
name = "roifile"
-version = "2025.5.10"
-source = { registry = "https://pypi.org/simple" }
+version = "2026.2.10"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
dependencies = [
{ name = "numpy" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/ce/ee/8ab570959011aac7d5705b7c284ab90734aed297672711444e21c11ab4d0/roifile-2025.5.10.tar.gz", hash = "sha256:8e5d1f799695ac59f52d4d62bb9016de74ef2493b4d83e24ce7fb25e19f53cce", size = 18768, upload-time = "2025-05-10T19:22:33.042Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/59/cf/49d9a139e292415d669dccd0c283d99a4d027b9edbd533d7f56db8ff85e0/roifile-2026.2.10.tar.gz", hash = "sha256:87d2b685b00a4ca15125187727e634ae5c00b902362de302b592f892259f3751", size = 27311, upload-time = "2026-02-11T19:54:35.027Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/5b/9b/f689742c06f377e33090d78e370d013e7e062ab8ac407f03f1105ddfc934/roifile-2025.5.10-py3-none-any.whl", hash = "sha256:92ef64a775167542cd98c4beb7c5f867412c572d1a111f03ee0fede7fdf64155", size = 17557, upload-time = "2025-05-10T19:22:25.256Z" },
+ { url = "https://files.pythonhosted.org/packages/34/78/3cc84d58e13234861adf91d2897254cd84d3d4a90534d91c960b672e67d1/roifile-2026.2.10-py3-none-any.whl", hash = "sha256:5928a616b58a8ff1c9f89a0590db3616ccbf069c6692f8ecec2a7349f8bbb6ec", size = 21201, upload-time = "2026-02-11T19:54:34.137Z" },
]
[[package]]
name = "scalene"
version = "1.5.55"
-source = { registry = "https://pypi.org/simple" }
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
dependencies = [
{ name = "cloudpickle" },
{ name = "jinja2" },
@@ -1470,7 +1594,7 @@ wheels = [
[[package]]
name = "scikit-image"
version = "0.24.0"
-source = { registry = "https://pypi.org/simple" }
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
dependencies = [
{ name = "imageio" },
{ name = "lazy-loader" },
@@ -1497,88 +1621,88 @@ wheels = [
[[package]]
name = "scipy"
-version = "1.16.2"
-source = { registry = "https://pypi.org/simple" }
+version = "1.17.1"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
dependencies = [
{ name = "numpy" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/4c/3b/546a6f0bfe791bbb7f8d591613454d15097e53f906308ec6f7c1ce588e8e/scipy-1.16.2.tar.gz", hash = "sha256:af029b153d243a80afb6eabe40b0a07f8e35c9adc269c019f364ad747f826a6b", size = 30580599, upload-time = "2025-09-11T17:48:08.271Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/0b/ef/37ed4b213d64b48422df92560af7300e10fe30b5d665dd79932baebee0c6/scipy-1.16.2-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:6ab88ea43a57da1af33292ebd04b417e8e2eaf9d5aa05700be8d6e1b6501cd92", size = 36619956, upload-time = "2025-09-11T17:39:20.5Z" },
- { url = "https://files.pythonhosted.org/packages/85/ab/5c2eba89b9416961a982346a4d6a647d78c91ec96ab94ed522b3b6baf444/scipy-1.16.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:c95e96c7305c96ede73a7389f46ccd6c659c4da5ef1b2789466baeaed3622b6e", size = 28931117, upload-time = "2025-09-11T17:39:29.06Z" },
- { url = "https://files.pythonhosted.org/packages/80/d1/eed51ab64d227fe60229a2d57fb60ca5898cfa50ba27d4f573e9e5f0b430/scipy-1.16.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:87eb178db04ece7c698220d523c170125dbffebb7af0345e66c3554f6f60c173", size = 20921997, upload-time = "2025-09-11T17:39:34.892Z" },
- { url = "https://files.pythonhosted.org/packages/be/7c/33ea3e23bbadde96726edba6bf9111fb1969d14d9d477ffa202c67bec9da/scipy-1.16.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:4e409eac067dcee96a57fbcf424c13f428037827ec7ee3cb671ff525ca4fc34d", size = 23523374, upload-time = "2025-09-11T17:39:40.846Z" },
- { url = "https://files.pythonhosted.org/packages/96/0b/7399dc96e1e3f9a05e258c98d716196a34f528eef2ec55aad651ed136d03/scipy-1.16.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e574be127bb760f0dad24ff6e217c80213d153058372362ccb9555a10fc5e8d2", size = 33583702, upload-time = "2025-09-11T17:39:49.011Z" },
- { url = "https://files.pythonhosted.org/packages/1a/bc/a5c75095089b96ea72c1bd37a4497c24b581ec73db4ef58ebee142ad2d14/scipy-1.16.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f5db5ba6188d698ba7abab982ad6973265b74bb40a1efe1821b58c87f73892b9", size = 35883427, upload-time = "2025-09-11T17:39:57.406Z" },
- { url = "https://files.pythonhosted.org/packages/ab/66/e25705ca3d2b87b97fe0a278a24b7f477b4023a926847935a1a71488a6a6/scipy-1.16.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ec6e74c4e884104ae006d34110677bfe0098203a3fec2f3faf349f4cb05165e3", size = 36212940, upload-time = "2025-09-11T17:40:06.013Z" },
- { url = "https://files.pythonhosted.org/packages/d6/fd/0bb911585e12f3abdd603d721d83fc1c7492835e1401a0e6d498d7822b4b/scipy-1.16.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:912f46667d2d3834bc3d57361f854226475f695eb08c08a904aadb1c936b6a88", size = 38865092, upload-time = "2025-09-11T17:40:15.143Z" },
- { url = "https://files.pythonhosted.org/packages/d6/73/c449a7d56ba6e6f874183759f8483cde21f900a8be117d67ffbb670c2958/scipy-1.16.2-cp311-cp311-win_amd64.whl", hash = "sha256:91e9e8a37befa5a69e9cacbe0bcb79ae5afb4a0b130fd6db6ee6cc0d491695fa", size = 38687626, upload-time = "2025-09-11T17:40:24.041Z" },
- { url = "https://files.pythonhosted.org/packages/68/72/02f37316adf95307f5d9e579023c6899f89ff3a051fa079dbd6faafc48e5/scipy-1.16.2-cp311-cp311-win_arm64.whl", hash = "sha256:f3bf75a6dcecab62afde4d1f973f1692be013110cad5338007927db8da73249c", size = 25503506, upload-time = "2025-09-11T17:40:30.703Z" },
- { url = "https://files.pythonhosted.org/packages/b7/8d/6396e00db1282279a4ddd507c5f5e11f606812b608ee58517ce8abbf883f/scipy-1.16.2-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:89d6c100fa5c48472047632e06f0876b3c4931aac1f4291afc81a3644316bb0d", size = 36646259, upload-time = "2025-09-11T17:40:39.329Z" },
- { url = "https://files.pythonhosted.org/packages/3b/93/ea9edd7e193fceb8eef149804491890bde73fb169c896b61aa3e2d1e4e77/scipy-1.16.2-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:ca748936cd579d3f01928b30a17dc474550b01272d8046e3e1ee593f23620371", size = 28888976, upload-time = "2025-09-11T17:40:46.82Z" },
- { url = "https://files.pythonhosted.org/packages/91/4d/281fddc3d80fd738ba86fd3aed9202331180b01e2c78eaae0642f22f7e83/scipy-1.16.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:fac4f8ce2ddb40e2e3d0f7ec36d2a1e7f92559a2471e59aec37bd8d9de01fec0", size = 20879905, upload-time = "2025-09-11T17:40:52.545Z" },
- { url = "https://files.pythonhosted.org/packages/69/40/b33b74c84606fd301b2915f0062e45733c6ff5708d121dd0deaa8871e2d0/scipy-1.16.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:033570f1dcefd79547a88e18bccacff025c8c647a330381064f561d43b821232", size = 23553066, upload-time = "2025-09-11T17:40:59.014Z" },
- { url = "https://files.pythonhosted.org/packages/55/a7/22c739e2f21a42cc8f16bc76b47cff4ed54fbe0962832c589591c2abec34/scipy-1.16.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ea3421209bf00c8a5ef2227de496601087d8f638a2363ee09af059bd70976dc1", size = 33336407, upload-time = "2025-09-11T17:41:06.796Z" },
- { url = "https://files.pythonhosted.org/packages/53/11/a0160990b82999b45874dc60c0c183d3a3a969a563fffc476d5a9995c407/scipy-1.16.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f66bd07ba6f84cd4a380b41d1bf3c59ea488b590a2ff96744845163309ee8e2f", size = 35673281, upload-time = "2025-09-11T17:41:15.055Z" },
- { url = "https://files.pythonhosted.org/packages/96/53/7ef48a4cfcf243c3d0f1643f5887c81f29fdf76911c4e49331828e19fc0a/scipy-1.16.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5e9feab931bd2aea4a23388c962df6468af3d808ddf2d40f94a81c5dc38f32ef", size = 36004222, upload-time = "2025-09-11T17:41:23.868Z" },
- { url = "https://files.pythonhosted.org/packages/49/7f/71a69e0afd460049d41c65c630c919c537815277dfea214031005f474d78/scipy-1.16.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:03dfc75e52f72cf23ec2ced468645321407faad8f0fe7b1f5b49264adbc29cb1", size = 38664586, upload-time = "2025-09-11T17:41:31.021Z" },
- { url = "https://files.pythonhosted.org/packages/34/95/20e02ca66fb495a95fba0642fd48e0c390d0ece9b9b14c6e931a60a12dea/scipy-1.16.2-cp312-cp312-win_amd64.whl", hash = "sha256:0ce54e07bbb394b417457409a64fd015be623f36e330ac49306433ffe04bc97e", size = 38550641, upload-time = "2025-09-11T17:41:36.61Z" },
- { url = "https://files.pythonhosted.org/packages/92/ad/13646b9beb0a95528ca46d52b7babafbe115017814a611f2065ee4e61d20/scipy-1.16.2-cp312-cp312-win_arm64.whl", hash = "sha256:2a8ffaa4ac0df81a0b94577b18ee079f13fecdb924df3328fc44a7dc5ac46851", size = 25456070, upload-time = "2025-09-11T17:41:41.3Z" },
- { url = "https://files.pythonhosted.org/packages/c1/27/c5b52f1ee81727a9fc457f5ac1e9bf3d6eab311805ea615c83c27ba06400/scipy-1.16.2-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:84f7bf944b43e20b8a894f5fe593976926744f6c185bacfcbdfbb62736b5cc70", size = 36604856, upload-time = "2025-09-11T17:41:47.695Z" },
- { url = "https://files.pythonhosted.org/packages/32/a9/15c20d08e950b540184caa8ced675ba1128accb0e09c653780ba023a4110/scipy-1.16.2-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:5c39026d12edc826a1ef2ad35ad1e6d7f087f934bb868fc43fa3049c8b8508f9", size = 28864626, upload-time = "2025-09-11T17:41:52.642Z" },
- { url = "https://files.pythonhosted.org/packages/4c/fc/ea36098df653cca26062a627c1a94b0de659e97127c8491e18713ca0e3b9/scipy-1.16.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e52729ffd45b68777c5319560014d6fd251294200625d9d70fd8626516fc49f5", size = 20855689, upload-time = "2025-09-11T17:41:57.886Z" },
- { url = "https://files.pythonhosted.org/packages/dc/6f/d0b53be55727f3e6d7c72687ec18ea6d0047cf95f1f77488b99a2bafaee1/scipy-1.16.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:024dd4a118cccec09ca3209b7e8e614931a6ffb804b2a601839499cb88bdf925", size = 23512151, upload-time = "2025-09-11T17:42:02.303Z" },
- { url = "https://files.pythonhosted.org/packages/11/85/bf7dab56e5c4b1d3d8eef92ca8ede788418ad38a7dc3ff50262f00808760/scipy-1.16.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7a5dc7ee9c33019973a470556081b0fd3c9f4c44019191039f9769183141a4d9", size = 33329824, upload-time = "2025-09-11T17:42:07.549Z" },
- { url = "https://files.pythonhosted.org/packages/da/6a/1a927b14ddc7714111ea51f4e568203b2bb6ed59bdd036d62127c1a360c8/scipy-1.16.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c2275ff105e508942f99d4e3bc56b6ef5e4b3c0af970386ca56b777608ce95b7", size = 35681881, upload-time = "2025-09-11T17:42:13.255Z" },
- { url = "https://files.pythonhosted.org/packages/c1/5f/331148ea5780b4fcc7007a4a6a6ee0a0c1507a796365cc642d4d226e1c3a/scipy-1.16.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:af80196eaa84f033e48444d2e0786ec47d328ba00c71e4299b602235ffef9acb", size = 36006219, upload-time = "2025-09-11T17:42:18.765Z" },
- { url = "https://files.pythonhosted.org/packages/46/3a/e991aa9d2aec723b4a8dcfbfc8365edec5d5e5f9f133888067f1cbb7dfc1/scipy-1.16.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9fb1eb735fe3d6ed1f89918224e3385fbf6f9e23757cacc35f9c78d3b712dd6e", size = 38682147, upload-time = "2025-09-11T17:42:25.177Z" },
- { url = "https://files.pythonhosted.org/packages/a1/57/0f38e396ad19e41b4c5db66130167eef8ee620a49bc7d0512e3bb67e0cab/scipy-1.16.2-cp313-cp313-win_amd64.whl", hash = "sha256:fda714cf45ba43c9d3bae8f2585c777f64e3f89a2e073b668b32ede412d8f52c", size = 38520766, upload-time = "2025-09-11T17:43:25.342Z" },
- { url = "https://files.pythonhosted.org/packages/1b/a5/85d3e867b6822d331e26c862a91375bb7746a0b458db5effa093d34cdb89/scipy-1.16.2-cp313-cp313-win_arm64.whl", hash = "sha256:2f5350da923ccfd0b00e07c3e5cfb316c1c0d6c1d864c07a72d092e9f20db104", size = 25451169, upload-time = "2025-09-11T17:43:30.198Z" },
- { url = "https://files.pythonhosted.org/packages/09/d9/60679189bcebda55992d1a45498de6d080dcaf21ce0c8f24f888117e0c2d/scipy-1.16.2-cp313-cp313t-macosx_10_14_x86_64.whl", hash = "sha256:53d8d2ee29b925344c13bda64ab51785f016b1b9617849dac10897f0701b20c1", size = 37012682, upload-time = "2025-09-11T17:42:30.677Z" },
- { url = "https://files.pythonhosted.org/packages/83/be/a99d13ee4d3b7887a96f8c71361b9659ba4ef34da0338f14891e102a127f/scipy-1.16.2-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:9e05e33657efb4c6a9d23bd8300101536abd99c85cca82da0bffff8d8764d08a", size = 29389926, upload-time = "2025-09-11T17:42:35.845Z" },
- { url = "https://files.pythonhosted.org/packages/bf/0a/130164a4881cec6ca8c00faf3b57926f28ed429cd6001a673f83c7c2a579/scipy-1.16.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:7fe65b36036357003b3ef9d37547abeefaa353b237e989c21027b8ed62b12d4f", size = 21381152, upload-time = "2025-09-11T17:42:40.07Z" },
- { url = "https://files.pythonhosted.org/packages/47/a6/503ffb0310ae77fba874e10cddfc4a1280bdcca1d13c3751b8c3c2996cf8/scipy-1.16.2-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:6406d2ac6d40b861cccf57f49592f9779071655e9f75cd4f977fa0bdd09cb2e4", size = 23914410, upload-time = "2025-09-11T17:42:44.313Z" },
- { url = "https://files.pythonhosted.org/packages/fa/c7/1147774bcea50d00c02600aadaa919facbd8537997a62496270133536ed6/scipy-1.16.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ff4dc42bd321991fbf611c23fc35912d690f731c9914bf3af8f417e64aca0f21", size = 33481880, upload-time = "2025-09-11T17:42:49.325Z" },
- { url = "https://files.pythonhosted.org/packages/6a/74/99d5415e4c3e46b2586f30cdbecb95e101c7192628a484a40dd0d163811a/scipy-1.16.2-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:654324826654d4d9133e10675325708fb954bc84dae6e9ad0a52e75c6b1a01d7", size = 35791425, upload-time = "2025-09-11T17:42:54.711Z" },
- { url = "https://files.pythonhosted.org/packages/1b/ee/a6559de7c1cc710e938c0355d9d4fbcd732dac4d0d131959d1f3b63eb29c/scipy-1.16.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:63870a84cd15c44e65220eaed2dac0e8f8b26bbb991456a033c1d9abfe8a94f8", size = 36178622, upload-time = "2025-09-11T17:43:00.375Z" },
- { url = "https://files.pythonhosted.org/packages/4e/7b/f127a5795d5ba8ece4e0dce7d4a9fb7cb9e4f4757137757d7a69ab7d4f1a/scipy-1.16.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:fa01f0f6a3050fa6a9771a95d5faccc8e2f5a92b4a2e5440a0fa7264a2398472", size = 38783985, upload-time = "2025-09-11T17:43:06.661Z" },
- { url = "https://files.pythonhosted.org/packages/3e/9f/bc81c1d1e033951eb5912cd3750cc005943afa3e65a725d2443a3b3c4347/scipy-1.16.2-cp313-cp313t-win_amd64.whl", hash = "sha256:116296e89fba96f76353a8579820c2512f6e55835d3fad7780fece04367de351", size = 38631367, upload-time = "2025-09-11T17:43:14.44Z" },
- { url = "https://files.pythonhosted.org/packages/d6/5e/2cc7555fd81d01814271412a1d59a289d25f8b63208a0a16c21069d55d3e/scipy-1.16.2-cp313-cp313t-win_arm64.whl", hash = "sha256:98e22834650be81d42982360382b43b17f7ba95e0e6993e2a4f5b9ad9283a94d", size = 25787992, upload-time = "2025-09-11T17:43:19.745Z" },
- { url = "https://files.pythonhosted.org/packages/8b/ac/ad8951250516db71619f0bd3b2eb2448db04b720a003dd98619b78b692c0/scipy-1.16.2-cp314-cp314-macosx_10_14_x86_64.whl", hash = "sha256:567e77755019bb7461513c87f02bb73fb65b11f049aaaa8ca17cfaa5a5c45d77", size = 36595109, upload-time = "2025-09-11T17:43:35.713Z" },
- { url = "https://files.pythonhosted.org/packages/ff/f6/5779049ed119c5b503b0f3dc6d6f3f68eefc3a9190d4ad4c276f854f051b/scipy-1.16.2-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:17d9bb346194e8967296621208fcdfd39b55498ef7d2f376884d5ac47cec1a70", size = 28859110, upload-time = "2025-09-11T17:43:40.814Z" },
- { url = "https://files.pythonhosted.org/packages/82/09/9986e410ae38bf0a0c737ff8189ac81a93b8e42349aac009891c054403d7/scipy-1.16.2-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:0a17541827a9b78b777d33b623a6dcfe2ef4a25806204d08ead0768f4e529a88", size = 20850110, upload-time = "2025-09-11T17:43:44.981Z" },
- { url = "https://files.pythonhosted.org/packages/0d/ad/485cdef2d9215e2a7df6d61b81d2ac073dfacf6ae24b9ae87274c4e936ae/scipy-1.16.2-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:d7d4c6ba016ffc0f9568d012f5f1eb77ddd99412aea121e6fa8b4c3b7cbad91f", size = 23497014, upload-time = "2025-09-11T17:43:49.074Z" },
- { url = "https://files.pythonhosted.org/packages/a7/74/f6a852e5d581122b8f0f831f1d1e32fb8987776ed3658e95c377d308ed86/scipy-1.16.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9702c4c023227785c779cba2e1d6f7635dbb5b2e0936cdd3a4ecb98d78fd41eb", size = 33401155, upload-time = "2025-09-11T17:43:54.661Z" },
- { url = "https://files.pythonhosted.org/packages/d9/f5/61d243bbc7c6e5e4e13dde9887e84a5cbe9e0f75fd09843044af1590844e/scipy-1.16.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d1cdf0ac28948d225decdefcc45ad7dd91716c29ab56ef32f8e0d50657dffcc7", size = 35691174, upload-time = "2025-09-11T17:44:00.101Z" },
- { url = "https://files.pythonhosted.org/packages/03/99/59933956331f8cc57e406cdb7a483906c74706b156998f322913e789c7e1/scipy-1.16.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:70327d6aa572a17c2941cdfb20673f82e536e91850a2e4cb0c5b858b690e1548", size = 36070752, upload-time = "2025-09-11T17:44:05.619Z" },
- { url = "https://files.pythonhosted.org/packages/c6/7d/00f825cfb47ee19ef74ecf01244b43e95eae74e7e0ff796026ea7cd98456/scipy-1.16.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5221c0b2a4b58aa7c4ed0387d360fd90ee9086d383bb34d9f2789fafddc8a936", size = 38701010, upload-time = "2025-09-11T17:44:11.322Z" },
- { url = "https://files.pythonhosted.org/packages/e4/9f/b62587029980378304ba5a8563d376c96f40b1e133daacee76efdcae32de/scipy-1.16.2-cp314-cp314-win_amd64.whl", hash = "sha256:f5a85d7b2b708025af08f060a496dd261055b617d776fc05a1a1cc69e09fe9ff", size = 39360061, upload-time = "2025-09-11T17:45:09.814Z" },
- { url = "https://files.pythonhosted.org/packages/82/04/7a2f1609921352c7fbee0815811b5050582f67f19983096c4769867ca45f/scipy-1.16.2-cp314-cp314-win_arm64.whl", hash = "sha256:2cc73a33305b4b24556957d5857d6253ce1e2dcd67fa0ff46d87d1670b3e1e1d", size = 26126914, upload-time = "2025-09-11T17:45:14.73Z" },
- { url = "https://files.pythonhosted.org/packages/51/b9/60929ce350c16b221928725d2d1d7f86cf96b8bc07415547057d1196dc92/scipy-1.16.2-cp314-cp314t-macosx_10_14_x86_64.whl", hash = "sha256:9ea2a3fed83065d77367775d689401a703d0f697420719ee10c0780bcab594d8", size = 37013193, upload-time = "2025-09-11T17:44:16.757Z" },
- { url = "https://files.pythonhosted.org/packages/2a/41/ed80e67782d4bc5fc85a966bc356c601afddd175856ba7c7bb6d9490607e/scipy-1.16.2-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:7280d926f11ca945c3ef92ba960fa924e1465f8d07ce3a9923080363390624c4", size = 29390172, upload-time = "2025-09-11T17:44:21.783Z" },
- { url = "https://files.pythonhosted.org/packages/c4/a3/2f673ace4090452696ccded5f5f8efffb353b8f3628f823a110e0170b605/scipy-1.16.2-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:8afae1756f6a1fe04636407ef7dbece33d826a5d462b74f3d0eb82deabefd831", size = 21381326, upload-time = "2025-09-11T17:44:25.982Z" },
- { url = "https://files.pythonhosted.org/packages/42/bf/59df61c5d51395066c35836b78136accf506197617c8662e60ea209881e1/scipy-1.16.2-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:5c66511f29aa8d233388e7416a3f20d5cae7a2744d5cee2ecd38c081f4e861b3", size = 23915036, upload-time = "2025-09-11T17:44:30.527Z" },
- { url = "https://files.pythonhosted.org/packages/91/c3/edc7b300dc16847ad3672f1a6f3f7c5d13522b21b84b81c265f4f2760d4a/scipy-1.16.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:efe6305aeaa0e96b0ccca5ff647a43737d9a092064a3894e46c414db84bc54ac", size = 33484341, upload-time = "2025-09-11T17:44:35.981Z" },
- { url = "https://files.pythonhosted.org/packages/26/c7/24d1524e72f06ff141e8d04b833c20db3021020563272ccb1b83860082a9/scipy-1.16.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7f3a337d9ae06a1e8d655ee9d8ecb835ea5ddcdcbd8d23012afa055ab014f374", size = 35790840, upload-time = "2025-09-11T17:44:41.76Z" },
- { url = "https://files.pythonhosted.org/packages/aa/b7/5aaad984eeedd56858dc33d75efa59e8ce798d918e1033ef62d2708f2c3d/scipy-1.16.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bab3605795d269067d8ce78a910220262711b753de8913d3deeaedb5dded3bb6", size = 36174716, upload-time = "2025-09-11T17:44:47.316Z" },
- { url = "https://files.pythonhosted.org/packages/fd/c2/e276a237acb09824822b0ada11b028ed4067fdc367a946730979feacb870/scipy-1.16.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b0348d8ddb55be2a844c518cd8cc8deeeb8aeba707cf834db5758fc89b476a2c", size = 38790088, upload-time = "2025-09-11T17:44:53.011Z" },
- { url = "https://files.pythonhosted.org/packages/c6/b4/5c18a766e8353015439f3780f5fc473f36f9762edc1a2e45da3ff5a31b21/scipy-1.16.2-cp314-cp314t-win_amd64.whl", hash = "sha256:26284797e38b8a75e14ea6631d29bda11e76ceaa6ddb6fdebbfe4c4d90faf2f9", size = 39457455, upload-time = "2025-09-11T17:44:58.899Z" },
- { url = "https://files.pythonhosted.org/packages/97/30/2f9a5243008f76dfc5dee9a53dfb939d9b31e16ce4bd4f2e628bfc5d89d2/scipy-1.16.2-cp314-cp314t-win_arm64.whl", hash = "sha256:d2a4472c231328d4de38d5f1f68fdd6d28a615138f842580a8a321b5845cf779", size = 26448374, upload-time = "2025-09-11T17:45:03.45Z" },
+sdist = { url = "https://files.pythonhosted.org/packages/7a/97/5a3609c4f8d58b039179648e62dd220f89864f56f7357f5d4f45c29eb2cc/scipy-1.17.1.tar.gz", hash = "sha256:95d8e012d8cb8816c226aef832200b1d45109ed4464303e997c5b13122b297c0", size = 30573822, upload-time = "2026-02-23T00:26:24.851Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/df/75/b4ce781849931fef6fd529afa6b63711d5a733065722d0c3e2724af9e40a/scipy-1.17.1-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:1f95b894f13729334fb990162e911c9e5dc1ab390c58aa6cbecb389c5b5e28ec", size = 31613675, upload-time = "2026-02-23T00:16:00.13Z" },
+ { url = "https://files.pythonhosted.org/packages/f7/58/bccc2861b305abdd1b8663d6130c0b3d7cc22e8d86663edbc8401bfd40d4/scipy-1.17.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:e18f12c6b0bc5a592ed23d3f7b891f68fd7f8241d69b7883769eb5d5dfb52696", size = 28162057, upload-time = "2026-02-23T00:16:09.456Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/ee/18146b7757ed4976276b9c9819108adbc73c5aad636e5353e20746b73069/scipy-1.17.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:a3472cfbca0a54177d0faa68f697d8ba4c80bbdc19908c3465556d9f7efce9ee", size = 20334032, upload-time = "2026-02-23T00:16:17.358Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/e6/cef1cf3557f0c54954198554a10016b6a03b2ec9e22a4e1df734936bd99c/scipy-1.17.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:766e0dc5a616d026a3a1cffa379af959671729083882f50307e18175797b3dfd", size = 22709533, upload-time = "2026-02-23T00:16:25.791Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/60/8804678875fc59362b0fb759ab3ecce1f09c10a735680318ac30da8cd76b/scipy-1.17.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:744b2bf3640d907b79f3fd7874efe432d1cf171ee721243e350f55234b4cec4c", size = 33062057, upload-time = "2026-02-23T00:16:36.931Z" },
+ { url = "https://files.pythonhosted.org/packages/09/7d/af933f0f6e0767995b4e2d705a0665e454d1c19402aa7e895de3951ebb04/scipy-1.17.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43af8d1f3bea642559019edfe64e9b11192a8978efbd1539d7bc2aaa23d92de4", size = 35349300, upload-time = "2026-02-23T00:16:49.108Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/3d/7ccbbdcbb54c8fdc20d3b6930137c782a163fa626f0aef920349873421ba/scipy-1.17.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cd96a1898c0a47be4520327e01f874acfd61fb48a9420f8aa9f6483412ffa444", size = 35127333, upload-time = "2026-02-23T00:17:01.293Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/19/f926cb11c42b15ba08e3a71e376d816ac08614f769b4f47e06c3580c836a/scipy-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4eb6c25dd62ee8d5edf68a8e1c171dd71c292fdae95d8aeb3dd7d7de4c364082", size = 37741314, upload-time = "2026-02-23T00:17:12.576Z" },
+ { url = "https://files.pythonhosted.org/packages/95/da/0d1df507cf574b3f224ccc3d45244c9a1d732c81dcb26b1e8a766ae271a8/scipy-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:d30e57c72013c2a4fe441c2fcb8e77b14e152ad48b5464858e07e2ad9fbfceff", size = 36607512, upload-time = "2026-02-23T00:17:23.424Z" },
+ { url = "https://files.pythonhosted.org/packages/68/7f/bdd79ceaad24b671543ffe0ef61ed8e659440eb683b66f033454dcee90eb/scipy-1.17.1-cp311-cp311-win_arm64.whl", hash = "sha256:9ecb4efb1cd6e8c4afea0daa91a87fbddbce1b99d2895d151596716c0b2e859d", size = 24599248, upload-time = "2026-02-23T00:17:34.561Z" },
+ { url = "https://files.pythonhosted.org/packages/35/48/b992b488d6f299dbe3f11a20b24d3dda3d46f1a635ede1c46b5b17a7b163/scipy-1.17.1-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:35c3a56d2ef83efc372eaec584314bd0ef2e2f0d2adb21c55e6ad5b344c0dcb8", size = 31610954, upload-time = "2026-02-23T00:17:49.855Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/02/cf107b01494c19dc100f1d0b7ac3cc08666e96ba2d64db7626066cee895e/scipy-1.17.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:fcb310ddb270a06114bb64bbe53c94926b943f5b7f0842194d585c65eb4edd76", size = 28172662, upload-time = "2026-02-23T00:18:01.64Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/a9/599c28631bad314d219cf9ffd40e985b24d603fc8a2f4ccc5ae8419a535b/scipy-1.17.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:cc90d2e9c7e5c7f1a482c9875007c095c3194b1cfedca3c2f3291cdc2bc7c086", size = 20344366, upload-time = "2026-02-23T00:18:12.015Z" },
+ { url = "https://files.pythonhosted.org/packages/35/f5/906eda513271c8deb5af284e5ef0206d17a96239af79f9fa0aebfe0e36b4/scipy-1.17.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:c80be5ede8f3f8eded4eff73cc99a25c388ce98e555b17d31da05287015ffa5b", size = 22704017, upload-time = "2026-02-23T00:18:21.502Z" },
+ { url = "https://files.pythonhosted.org/packages/da/34/16f10e3042d2f1d6b66e0428308ab52224b6a23049cb2f5c1756f713815f/scipy-1.17.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e19ebea31758fac5893a2ac360fedd00116cbb7628e650842a6691ba7ca28a21", size = 32927842, upload-time = "2026-02-23T00:18:35.367Z" },
+ { url = "https://files.pythonhosted.org/packages/01/8e/1e35281b8ab6d5d72ebe9911edcdffa3f36b04ed9d51dec6dd140396e220/scipy-1.17.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:02ae3b274fde71c5e92ac4d54bc06c42d80e399fec704383dcd99b301df37458", size = 35235890, upload-time = "2026-02-23T00:18:49.188Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/5c/9d7f4c88bea6e0d5a4f1bc0506a53a00e9fcb198de372bfe4d3652cef482/scipy-1.17.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8a604bae87c6195d8b1045eddece0514d041604b14f2727bbc2b3020172045eb", size = 35003557, upload-time = "2026-02-23T00:18:54.74Z" },
+ { url = "https://files.pythonhosted.org/packages/65/94/7698add8f276dbab7a9de9fb6b0e02fc13ee61d51c7c3f85ac28b65e1239/scipy-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f590cd684941912d10becc07325a3eeb77886fe981415660d9265c4c418d0bea", size = 37625856, upload-time = "2026-02-23T00:19:00.307Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/84/dc08d77fbf3d87d3ee27f6a0c6dcce1de5829a64f2eae85a0ecc1f0daa73/scipy-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:41b71f4a3a4cab9d366cd9065b288efc4d4f3c0b37a91a8e0947fb5bd7f31d87", size = 36549682, upload-time = "2026-02-23T00:19:07.67Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/98/fe9ae9ffb3b54b62559f52dedaebe204b408db8109a8c66fdd04869e6424/scipy-1.17.1-cp312-cp312-win_arm64.whl", hash = "sha256:f4115102802df98b2b0db3cce5cb9b92572633a1197c77b7553e5203f284a5b3", size = 24547340, upload-time = "2026-02-23T00:19:12.024Z" },
+ { url = "https://files.pythonhosted.org/packages/76/27/07ee1b57b65e92645f219b37148a7e7928b82e2b5dbeccecb4dff7c64f0b/scipy-1.17.1-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:5e3c5c011904115f88a39308379c17f91546f77c1667cea98739fe0fccea804c", size = 31590199, upload-time = "2026-02-23T00:19:17.192Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/ae/db19f8ab842e9b724bf5dbb7db29302a91f1e55bc4d04b1025d6d605a2c5/scipy-1.17.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:6fac755ca3d2c3edcb22f479fceaa241704111414831ddd3bc6056e18516892f", size = 28154001, upload-time = "2026-02-23T00:19:22.241Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/58/3ce96251560107b381cbd6e8413c483bbb1228a6b919fa8652b0d4090e7f/scipy-1.17.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:7ff200bf9d24f2e4d5dc6ee8c3ac64d739d3a89e2326ba68aaf6c4a2b838fd7d", size = 20325719, upload-time = "2026-02-23T00:19:26.329Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/83/15087d945e0e4d48ce2377498abf5ad171ae013232ae31d06f336e64c999/scipy-1.17.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:4b400bdc6f79fa02a4d86640310dde87a21fba0c979efff5248908c6f15fad1b", size = 22683595, upload-time = "2026-02-23T00:19:30.304Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/e0/e58fbde4a1a594c8be8114eb4aac1a55bcd6587047efc18a61eb1f5c0d30/scipy-1.17.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2b64ca7d4aee0102a97f3ba22124052b4bd2152522355073580bf4845e2550b6", size = 32896429, upload-time = "2026-02-23T00:19:35.536Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/5f/f17563f28ff03c7b6799c50d01d5d856a1d55f2676f537ca8d28c7f627cd/scipy-1.17.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:581b2264fc0aa555f3f435a5944da7504ea3a065d7029ad60e7c3d1ae09c5464", size = 35203952, upload-time = "2026-02-23T00:19:42.259Z" },
+ { url = "https://files.pythonhosted.org/packages/8d/a5/9afd17de24f657fdfe4df9a3f1ea049b39aef7c06000c13db1530d81ccca/scipy-1.17.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:beeda3d4ae615106d7094f7e7cef6218392e4465cc95d25f900bebabfded0950", size = 34979063, upload-time = "2026-02-23T00:19:47.547Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/13/88b1d2384b424bf7c924f2038c1c409f8d88bb2a8d49d097861dd64a57b2/scipy-1.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6609bc224e9568f65064cfa72edc0f24ee6655b47575954ec6339534b2798369", size = 37598449, upload-time = "2026-02-23T00:19:53.238Z" },
+ { url = "https://files.pythonhosted.org/packages/35/e5/d6d0e51fc888f692a35134336866341c08655d92614f492c6860dc45bb2c/scipy-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:37425bc9175607b0268f493d79a292c39f9d001a357bebb6b88fdfaff13f6448", size = 36510943, upload-time = "2026-02-23T00:20:50.89Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/fd/3be73c564e2a01e690e19cc618811540ba5354c67c8680dce3281123fb79/scipy-1.17.1-cp313-cp313-win_arm64.whl", hash = "sha256:5cf36e801231b6a2059bf354720274b7558746f3b1a4efb43fcf557ccd484a87", size = 24545621, upload-time = "2026-02-23T00:20:55.871Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/6b/17787db8b8114933a66f9dcc479a8272e4b4da75fe03b0c282f7b0ade8cd/scipy-1.17.1-cp313-cp313t-macosx_10_14_x86_64.whl", hash = "sha256:d59c30000a16d8edc7e64152e30220bfbd724c9bbb08368c054e24c651314f0a", size = 31936708, upload-time = "2026-02-23T00:19:58.694Z" },
+ { url = "https://files.pythonhosted.org/packages/38/2e/524405c2b6392765ab1e2b722a41d5da33dc5c7b7278184a8ad29b6cb206/scipy-1.17.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:010f4333c96c9bb1a4516269e33cb5917b08ef2166d5556ca2fd9f082a9e6ea0", size = 28570135, upload-time = "2026-02-23T00:20:03.934Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/c3/5bd7199f4ea8556c0c8e39f04ccb014ac37d1468e6cfa6a95c6b3562b76e/scipy-1.17.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:2ceb2d3e01c5f1d83c4189737a42d9cb2fc38a6eeed225e7515eef71ad301dce", size = 20741977, upload-time = "2026-02-23T00:20:07.935Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/b8/8ccd9b766ad14c78386599708eb745f6b44f08400a5fd0ade7cf89b6fc93/scipy-1.17.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:844e165636711ef41f80b4103ed234181646b98a53c8f05da12ca5ca289134f6", size = 23029601, upload-time = "2026-02-23T00:20:12.161Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/a0/3cb6f4d2fb3e17428ad2880333cac878909ad1a89f678527b5328b93c1d4/scipy-1.17.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:158dd96d2207e21c966063e1635b1063cd7787b627b6f07305315dd73d9c679e", size = 33019667, upload-time = "2026-02-23T00:20:17.208Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/c3/2d834a5ac7bf3a0c806ad1508efc02dda3c8c61472a56132d7894c312dea/scipy-1.17.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:74cbb80d93260fe2ffa334efa24cb8f2f0f622a9b9febf8b483c0b865bfb3475", size = 35264159, upload-time = "2026-02-23T00:20:23.087Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/77/d3ed4becfdbd217c52062fafe35a72388d1bd82c2d0ba5ca19d6fcc93e11/scipy-1.17.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:dbc12c9f3d185f5c737d801da555fb74b3dcfa1a50b66a1a93e09190f41fab50", size = 35102771, upload-time = "2026-02-23T00:20:28.636Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/12/d19da97efde68ca1ee5538bb261d5d2c062f0c055575128f11a2730e3ac1/scipy-1.17.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:94055a11dfebe37c656e70317e1996dc197e1a15bbcc351bcdd4610e128fe1ca", size = 37665910, upload-time = "2026-02-23T00:20:34.743Z" },
+ { url = "https://files.pythonhosted.org/packages/06/1c/1172a88d507a4baaf72c5a09bb6c018fe2ae0ab622e5830b703a46cc9e44/scipy-1.17.1-cp313-cp313t-win_amd64.whl", hash = "sha256:e30bdeaa5deed6bc27b4cc490823cd0347d7dae09119b8803ae576ea0ce52e4c", size = 36562980, upload-time = "2026-02-23T00:20:40.575Z" },
+ { url = "https://files.pythonhosted.org/packages/70/b0/eb757336e5a76dfa7911f63252e3b7d1de00935d7705cf772db5b45ec238/scipy-1.17.1-cp313-cp313t-win_arm64.whl", hash = "sha256:a720477885a9d2411f94a93d16f9d89bad0f28ca23c3f8daa521e2dcc3f44d49", size = 24856543, upload-time = "2026-02-23T00:20:45.313Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/83/333afb452af6f0fd70414dc04f898647ee1423979ce02efa75c3b0f2c28e/scipy-1.17.1-cp314-cp314-macosx_10_14_x86_64.whl", hash = "sha256:a48a72c77a310327f6a3a920092fa2b8fd03d7deaa60f093038f22d98e096717", size = 31584510, upload-time = "2026-02-23T00:21:01.015Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/a6/d05a85fd51daeb2e4ea71d102f15b34fedca8e931af02594193ae4fd25f7/scipy-1.17.1-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:45abad819184f07240d8a696117a7aacd39787af9e0b719d00285549ed19a1e9", size = 28170131, upload-time = "2026-02-23T00:21:05.888Z" },
+ { url = "https://files.pythonhosted.org/packages/db/7b/8624a203326675d7746a254083a187398090a179335b2e4a20e2ddc46e83/scipy-1.17.1-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:3fd1fcdab3ea951b610dc4cef356d416d5802991e7e32b5254828d342f7b7e0b", size = 20342032, upload-time = "2026-02-23T00:21:09.904Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/35/2c342897c00775d688d8ff3987aced3426858fd89d5a0e26e020b660b301/scipy-1.17.1-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:7bdf2da170b67fdf10bca777614b1c7d96ae3ca5794fd9587dce41eb2966e866", size = 22678766, upload-time = "2026-02-23T00:21:14.313Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/f2/7cdb8eb308a1a6ae1e19f945913c82c23c0c442a462a46480ce487fdc0ac/scipy-1.17.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:adb2642e060a6549c343603a3851ba76ef0b74cc8c079a9a58121c7ec9fe2350", size = 32957007, upload-time = "2026-02-23T00:21:19.663Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/2e/7eea398450457ecb54e18e9d10110993fa65561c4f3add5e8eccd2b9cd41/scipy-1.17.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eee2cfda04c00a857206a4330f0c5e3e56535494e30ca445eb19ec624ae75118", size = 35221333, upload-time = "2026-02-23T00:21:25.278Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/77/5b8509d03b77f093a0d52e606d3c4f79e8b06d1d38c441dacb1e26cacf46/scipy-1.17.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d2650c1fb97e184d12d8ba010493ee7b322864f7d3d00d3f9bb97d9c21de4068", size = 35042066, upload-time = "2026-02-23T00:21:31.358Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/df/18f80fb99df40b4070328d5ae5c596f2f00fffb50167e31439e932f29e7d/scipy-1.17.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:08b900519463543aa604a06bec02461558a6e1cef8fdbb8098f77a48a83c8118", size = 37612763, upload-time = "2026-02-23T00:21:37.247Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/39/f0e8ea762a764a9dc52aa7dabcfad51a354819de1f0d4652b6a1122424d6/scipy-1.17.1-cp314-cp314-win_amd64.whl", hash = "sha256:3877ac408e14da24a6196de0ddcace62092bfc12a83823e92e49e40747e52c19", size = 37290984, upload-time = "2026-02-23T00:22:35.023Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/56/fe201e3b0f93d1a8bcf75d3379affd228a63d7e2d80ab45467a74b494947/scipy-1.17.1-cp314-cp314-win_arm64.whl", hash = "sha256:f8885db0bc2bffa59d5c1b72fad7a6a92d3e80e7257f967dd81abb553a90d293", size = 25192877, upload-time = "2026-02-23T00:22:39.798Z" },
+ { url = "https://files.pythonhosted.org/packages/96/ad/f8c414e121f82e02d76f310f16db9899c4fcde36710329502a6b2a3c0392/scipy-1.17.1-cp314-cp314t-macosx_10_14_x86_64.whl", hash = "sha256:1cc682cea2ae55524432f3cdff9e9a3be743d52a7443d0cba9017c23c87ae2f6", size = 31949750, upload-time = "2026-02-23T00:21:42.289Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/b0/c741e8865d61b67c81e255f4f0a832846c064e426636cd7de84e74d209be/scipy-1.17.1-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:2040ad4d1795a0ae89bfc7e8429677f365d45aa9fd5e4587cf1ea737f927b4a1", size = 28585858, upload-time = "2026-02-23T00:21:47.706Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/1b/3985219c6177866628fa7c2595bfd23f193ceebbe472c98a08824b9466ff/scipy-1.17.1-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:131f5aaea57602008f9822e2115029b55d4b5f7c070287699fe45c661d051e39", size = 20757723, upload-time = "2026-02-23T00:21:52.039Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/19/2a04aa25050d656d6f7b9e7b685cc83d6957fb101665bfd9369ca6534563/scipy-1.17.1-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:9cdc1a2fcfd5c52cfb3045feb399f7b3ce822abdde3a193a6b9a60b3cb5854ca", size = 23043098, upload-time = "2026-02-23T00:21:56.185Z" },
+ { url = "https://files.pythonhosted.org/packages/86/f1/3383beb9b5d0dbddd030335bf8a8b32d4317185efe495374f134d8be6cce/scipy-1.17.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e3dcd57ab780c741fde8dc68619de988b966db759a3c3152e8e9142c26295ad", size = 33030397, upload-time = "2026-02-23T00:22:01.404Z" },
+ { url = "https://files.pythonhosted.org/packages/41/68/8f21e8a65a5a03f25a79165ec9d2b28c00e66dc80546cf5eb803aeeff35b/scipy-1.17.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a9956e4d4f4a301ebf6cde39850333a6b6110799d470dbbb1e25326ac447f52a", size = 35281163, upload-time = "2026-02-23T00:22:07.024Z" },
+ { url = "https://files.pythonhosted.org/packages/84/8d/c8a5e19479554007a5632ed7529e665c315ae7492b4f946b0deb39870e39/scipy-1.17.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:a4328d245944d09fd639771de275701ccadf5f781ba0ff092ad141e017eccda4", size = 35116291, upload-time = "2026-02-23T00:22:12.585Z" },
+ { url = "https://files.pythonhosted.org/packages/52/52/e57eceff0e342a1f50e274264ed47497b59e6a4e3118808ee58ddda7b74a/scipy-1.17.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a77cbd07b940d326d39a1d1b37817e2ee4d79cb30e7338f3d0cddffae70fcaa2", size = 37682317, upload-time = "2026-02-23T00:22:18.513Z" },
+ { url = "https://files.pythonhosted.org/packages/11/2f/b29eafe4a3fbc3d6de9662b36e028d5f039e72d345e05c250e121a230dd4/scipy-1.17.1-cp314-cp314t-win_amd64.whl", hash = "sha256:eb092099205ef62cd1782b006658db09e2fed75bffcae7cc0d44052d8aa0f484", size = 37345327, upload-time = "2026-02-23T00:22:24.442Z" },
+ { url = "https://files.pythonhosted.org/packages/07/39/338d9219c4e87f3e708f18857ecd24d22a0c3094752393319553096b98af/scipy-1.17.1-cp314-cp314t-win_arm64.whl", hash = "sha256:200e1050faffacc162be6a486a984a0497866ec54149a01270adc8a59b7c7d21", size = 25489165, upload-time = "2026-02-23T00:22:29.563Z" },
]
[[package]]
name = "setuptools"
-version = "80.9.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958, upload-time = "2025-05-27T00:56:51.443Z" }
+version = "81.0.0"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/0d/1c/73e719955c59b8e424d015ab450f51c0af856ae46ea2da83eba51cc88de1/setuptools-81.0.0.tar.gz", hash = "sha256:487b53915f52501f0a79ccfd0c02c165ffe06631443a886740b91af4b7a5845a", size = 1198299, upload-time = "2026-02-06T21:10:39.601Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486, upload-time = "2025-05-27T00:56:49.664Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/e3/c164c88b2e5ce7b24d667b9bd83589cf4f3520d97cad01534cd3c4f55fdb/setuptools-81.0.0-py3-none-any.whl", hash = "sha256:fdd925d5c5d9f62e4b74b30d6dd7828ce236fd6ed998a08d81de62ce5a6310d6", size = 1062021, upload-time = "2026-02-06T21:10:37.175Z" },
]
[[package]]
name = "six"
version = "1.17.0"
-source = { registry = "https://pypi.org/simple" }
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" },
@@ -1587,7 +1711,7 @@ wheels = [
[[package]]
name = "sympy"
version = "1.14.0"
-source = { registry = "https://pypi.org/simple" }
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
dependencies = [
{ name = "mpmath" },
]
@@ -1599,7 +1723,7 @@ wheels = [
[[package]]
name = "tifffile"
version = "2024.12.12"
-source = { registry = "https://pypi.org/simple" }
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
dependencies = [
{ name = "numpy" },
]
@@ -1610,98 +1734,95 @@ wheels = [
[[package]]
name = "toolz"
-version = "1.0.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/8a/0b/d80dfa675bf592f636d1ea0b835eab4ec8df6e9415d8cfd766df54456123/toolz-1.0.0.tar.gz", hash = "sha256:2c86e3d9a04798ac556793bced838816296a2f085017664e4995cb40a1047a02", size = 66790, upload-time = "2024-10-04T16:17:04.001Z" }
+version = "1.1.0"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/11/d6/114b492226588d6ff54579d95847662fc69196bdeec318eb45393b24c192/toolz-1.1.0.tar.gz", hash = "sha256:27a5c770d068c110d9ed9323f24f1543e83b2f300a687b7891c1a6d56b697b5b", size = 52613, upload-time = "2025-10-17T04:03:21.661Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/03/98/eb27cc78ad3af8e302c9d8ff4977f5026676e130d28dd7578132a457170c/toolz-1.0.0-py3-none-any.whl", hash = "sha256:292c8f1c4e7516bf9086f8850935c799a874039c8bcf959d47b600e4c44a6236", size = 56383, upload-time = "2024-10-04T16:17:01.533Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/12/5911ae3eeec47800503a238d971e51722ccea5feb8569b735184d5fcdbc0/toolz-1.1.0-py3-none-any.whl", hash = "sha256:15ccc861ac51c53696de0a5d6d4607f99c210739caf987b5d2054f3efed429d8", size = 58093, upload-time = "2025-10-17T04:03:20.435Z" },
]
[[package]]
name = "torch"
-version = "2.9.0"
-source = { registry = "https://pypi.org/simple" }
+version = "2.11.0"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
dependencies = [
+ { name = "cuda-bindings", marker = "sys_platform == 'linux'" },
+ { name = "cuda-toolkit", extra = ["cublas", "cudart", "cufft", "cufile", "cupti", "curand", "cusolver", "cusparse", "nvjitlink", "nvrtc", "nvtx"], marker = "sys_platform == 'linux'" },
{ name = "filelock" },
{ name = "fsspec" },
{ name = "jinja2" },
{ name = "networkx" },
- { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
- { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
- { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
- { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
- { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
- { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
- { name = "nvidia-cufile-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
- { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
- { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
- { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
- { name = "nvidia-cusparselt-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
- { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
- { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
- { name = "nvidia-nvshmem-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
- { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
- { name = "setuptools", marker = "python_full_version >= '3.12'" },
+ { name = "nvidia-cudnn-cu13", marker = "sys_platform == 'linux'" },
+ { name = "nvidia-cusparselt-cu13", marker = "sys_platform == 'linux'" },
+ { name = "nvidia-nccl-cu13", marker = "sys_platform == 'linux'" },
+ { name = "nvidia-nvshmem-cu13", marker = "sys_platform == 'linux'" },
+ { name = "setuptools" },
{ name = "sympy" },
- { name = "triton", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
+ { name = "triton", marker = "sys_platform == 'linux'" },
{ name = "typing-extensions" },
]
wheels = [
- { url = "https://files.pythonhosted.org/packages/58/fe/334225e6330e672b36aef23d77451fa906ea12881570c08638a91331a212/torch-2.9.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:c596708b5105d0b199215acf0c9be7c1db5f1680d88eddadf4b75a299259a677", size = 104230578, upload-time = "2025-10-15T15:46:08.182Z" },
- { url = "https://files.pythonhosted.org/packages/05/cc/49566caaa218872ec9a2912456f470ff92649894a4bc2e5274aa9ef87c4a/torch-2.9.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:51de31219c97c51cf4bf2be94d622e3deb5dcc526c6dc00e97c17eaec0fc1d67", size = 899815990, upload-time = "2025-10-15T15:48:03.336Z" },
- { url = "https://files.pythonhosted.org/packages/74/25/e9ab21d5925b642d008f139d4a3c9664fc9ee1faafca22913c080cc4c0a5/torch-2.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:dd515c70059afd95f48b8192733764c08ca37a1d19803af6401b5ecad7c8676e", size = 109313698, upload-time = "2025-10-15T15:46:12.425Z" },
- { url = "https://files.pythonhosted.org/packages/b3/b7/205ef3e94de636feffd64b28bb59a0dfac0771221201b9871acf9236f5ca/torch-2.9.0-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:614a185e4986326d526a91210c8fc1397e76e8cfafa78baf6296a790e53a9eec", size = 74463678, upload-time = "2025-10-15T15:46:29.779Z" },
- { url = "https://files.pythonhosted.org/packages/d1/d3/3985739f3b8e88675127bf70f82b3a48ae083e39cda56305dbd90398fec0/torch-2.9.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:e5f7af1dc4c0a7c4a260c2534f41ddaf209714f7c89145e644c44712fbd6b642", size = 104107898, upload-time = "2025-10-15T15:46:20.883Z" },
- { url = "https://files.pythonhosted.org/packages/a5/4b/f4bb2e6c25d0272f798cd6d7a04ed315da76cec68c602d87040c7847287f/torch-2.9.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:01cff95ecd9a212ea2f141db28acccdceb6a4c54f64e6c51091146f5e2a772c6", size = 899738273, upload-time = "2025-10-15T15:50:04.188Z" },
- { url = "https://files.pythonhosted.org/packages/66/11/c1c5ba6691cda6279087c35bd626536e4fd29521fe740abf5008377a9a02/torch-2.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:4582b162f541651f0cb184d3e291c05c2f556c7117c64a9873e2ee158d40062b", size = 109280887, upload-time = "2025-10-15T15:46:26.228Z" },
- { url = "https://files.pythonhosted.org/packages/dd/5f/b85bd8c05312d71de9402bf5868d217c38827cfd09d8f8514e5be128a52b/torch-2.9.0-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:33f58e9a102a91259af289d50525c30323b5c9ae1d31322b6447c0814da68695", size = 74478983, upload-time = "2025-10-15T15:46:39.406Z" },
- { url = "https://files.pythonhosted.org/packages/c2/1c/90eb13833cdf4969ea9707586d7b57095c3b6e2b223a7256bf111689bcb8/torch-2.9.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:c30a17fc83eeab346913e237c64b15b5ba6407fff812f6c541e322e19bc9ea0e", size = 104111330, upload-time = "2025-10-15T15:46:35.238Z" },
- { url = "https://files.pythonhosted.org/packages/0e/21/2254c54b8d523592c25ef4434769aa23e29b1e6bf5f4c0ad9e27bf442927/torch-2.9.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:8f25033b8667b57857dfd01458fbf2a9e6a6df1f8def23aef0dc46292f6aa642", size = 899750243, upload-time = "2025-10-15T15:48:57.459Z" },
- { url = "https://files.pythonhosted.org/packages/b7/a5/5cb94fa4fd1e78223455c23c200f30f6dc10c6d4a2bcc8f6e7f2a2588370/torch-2.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:d037f1b4ffd25013be4a7bf3651a0a910c68554956c7b2c92ebe87c76475dece", size = 109284513, upload-time = "2025-10-15T15:46:45.061Z" },
- { url = "https://files.pythonhosted.org/packages/66/e8/fc414d8656250ee46120b44836ffbb3266343db424b3e18ca79ebbf69d4f/torch-2.9.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e4e5b5cba837a2a8d1a497ba9a58dae46fa392593eaa13b871c42f71847503a5", size = 74830362, upload-time = "2025-10-15T15:46:48.983Z" },
- { url = "https://files.pythonhosted.org/packages/ed/5f/9474c98fc5ae0cd04b9466035428cd360e6611a86b8352a0fc2fa504acdc/torch-2.9.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:64693568f5dc4dbd5f880a478b1cea0201cc6b510d91d1bc54fea86ac5d1a637", size = 104144940, upload-time = "2025-10-15T15:47:29.076Z" },
- { url = "https://files.pythonhosted.org/packages/2d/5a/8e0c1cf57830172c109d4bd6be2708cabeaf550983eee7029291322447a0/torch-2.9.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:f8ed31ddd7d10bfb3fbe0b9fe01b1243577f13d75e6f4a0839a283915ce3791e", size = 899744054, upload-time = "2025-10-15T15:48:29.864Z" },
- { url = "https://files.pythonhosted.org/packages/6d/28/82c28b30fcb4b7c9cdd995763d18bbb830d6521356712faebbad92ffa61d/torch-2.9.0-cp313-cp313t-win_amd64.whl", hash = "sha256:eff527d4e4846e6f70d2afd8058b73825761203d66576a7e04ea2ecfebcb4ab8", size = 109517546, upload-time = "2025-10-15T15:47:33.395Z" },
- { url = "https://files.pythonhosted.org/packages/ff/c3/a91f96ec74347fa5fd24453fa514bc61c61ecc79196fa760b012a1873d96/torch-2.9.0-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:f8877779cf56d1ce431a7636703bdb13307f5960bb1af49716d8b179225e0e6a", size = 74480732, upload-time = "2025-10-15T15:47:38.002Z" },
- { url = "https://files.pythonhosted.org/packages/5c/73/9f70af34b334a7e0ef496ceec96b7ec767bd778ea35385ce6f77557534d1/torch-2.9.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7e614fae699838038d888729f82b687c03413c5989ce2a9481f9a7e7a396e0bb", size = 74433037, upload-time = "2025-10-15T15:47:41.894Z" },
- { url = "https://files.pythonhosted.org/packages/b7/84/37cf88625901934c97109e583ecc21777d21c6f54cda97a7e5bbad1ee2f2/torch-2.9.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:dfb5b8cd310ba3436c7e14e8b7833ef658cf3045e50d2bdaed23c8fc517065eb", size = 104116482, upload-time = "2025-10-15T15:47:46.266Z" },
- { url = "https://files.pythonhosted.org/packages/56/8e/ca8b17866943a8d4f4664d402ea84210aa274588b4c5d89918f5caa24eec/torch-2.9.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:b3d29524993a478e46f5d598b249cd824b7ed98d7fba538bd9c4cde6c803948f", size = 899746916, upload-time = "2025-10-15T15:50:40.294Z" },
- { url = "https://files.pythonhosted.org/packages/43/65/3b17c0fbbdab6501c5b320a52a648628d0d44e7379f64e27d9eef701b6bf/torch-2.9.0-cp314-cp314-win_amd64.whl", hash = "sha256:71c7578984f5ec0eb645eb4816ac8435fcf3e3e2ae1901bcd2f519a9cafb5125", size = 109275151, upload-time = "2025-10-15T15:49:20.715Z" },
- { url = "https://files.pythonhosted.org/packages/83/36/74f8c051f785500396e42f93542422422dfd874a174f21f8d955d36e5d64/torch-2.9.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:71d9309aee457bbe0b164bce2111cd911c4ed4e847e65d5077dbbcd3aba6befc", size = 74823353, upload-time = "2025-10-15T15:49:16.59Z" },
- { url = "https://files.pythonhosted.org/packages/62/51/dc3b4e2f9ba98ae27238f0153ca098bf9340b2dafcc67fde645d496dfc2a/torch-2.9.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:c08fb654d783899e204a32cca758a7ce8a45b2d78eeb89517cc937088316f78e", size = 104140340, upload-time = "2025-10-15T15:50:19.67Z" },
- { url = "https://files.pythonhosted.org/packages/c0/8d/b00657f8141ac16af7bb6cda2e67de18499a3263b78d516b9a93fcbc98e3/torch-2.9.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:ec8feb0099b2daa5728fbc7abb0b05730fd97e0f359ff8bda09865aaa7bd7d4b", size = 899731750, upload-time = "2025-10-15T15:49:36.673Z" },
- { url = "https://files.pythonhosted.org/packages/fc/29/bd361e0cbb2c79ce6450f42643aaf6919956f89923a50571b0ebfe92d142/torch-2.9.0-cp314-cp314t-win_amd64.whl", hash = "sha256:695ba920f234ad4170c9c50e28d56c848432f8f530e6bc7f88fcb15ddf338e75", size = 109503850, upload-time = "2025-10-15T15:50:24.118Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/0d/98b410492609e34a155fa8b121b55c7dca229f39636851c3a9ec20edea21/torch-2.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7b6a60d48062809f58595509c524b88e6ddec3ebe25833d6462eeab81e5f2ce4", size = 80529712, upload-time = "2026-03-23T18:12:02.608Z" },
+ { url = "https://files.pythonhosted.org/packages/84/03/acea680005f098f79fd70c1d9d5ccc0cb4296ec2af539a0450108232fc0c/torch-2.11.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:d91aac77f24082809d2c5a93f52a5f085032740a1ebc9252a7b052ef5a4fddc6", size = 419718178, upload-time = "2026-03-23T18:10:46.675Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/8b/d7be22fbec9ffee6cff31a39f8750d4b3a65d349a286cf4aec74c2375662/torch-2.11.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:7aa2f9bbc6d4595ba72138026b2074be1233186150e9292865e04b7a63b8c67a", size = 530604548, upload-time = "2026-03-23T18:10:03.569Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/bd/9912d30b68845256aabbb4a40aeefeef3c3b20db5211ccda653544ada4b6/torch-2.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:73e24aaf8f36ab90d95cd1761208b2eb70841c2a9ca1a3f9061b39fc5331b708", size = 114519675, upload-time = "2026-03-23T18:11:52.995Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/8b/69e3008d78e5cee2b30183340cc425081b78afc5eff3d080daab0adda9aa/torch-2.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4b5866312ee6e52ea625cd211dcb97d6a2cdc1131a5f15cc0d87eec948f6dd34", size = 80606338, upload-time = "2026-03-23T18:11:34.781Z" },
+ { url = "https://files.pythonhosted.org/packages/13/16/42e5915ebe4868caa6bac83a8ed59db57f12e9a61b7d749d584776ed53d5/torch-2.11.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f99924682ef0aa6a4ab3b1b76f40dc6e273fca09f367d15a524266db100a723f", size = 419731115, upload-time = "2026-03-23T18:11:06.944Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/c9/82638ef24d7877510f83baf821f5619a61b45568ce21c0a87a91576510aa/torch-2.11.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:0f68f4ac6d95d12e896c3b7a912b5871619542ec54d3649cf48cc1edd4dd2756", size = 530712279, upload-time = "2026-03-23T18:10:31.481Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/ff/6756f1c7ee302f6d202120e0f4f05b432b839908f9071157302cedfc5232/torch-2.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:fbf39280699d1b869f55eac536deceaa1b60bd6788ba74f399cc67e60a5fab10", size = 114556047, upload-time = "2026-03-23T18:10:55.931Z" },
+ { url = "https://files.pythonhosted.org/packages/87/89/5ea6722763acee56b045435fb84258db7375c48165ec8be7880ab2b281c5/torch-2.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1e6debd97ccd3205bbb37eb806a9d8219e1139d15419982c09e23ef7d4369d18", size = 80606801, upload-time = "2026-03-23T18:10:18.649Z" },
+ { url = "https://files.pythonhosted.org/packages/32/d1/8ed2173589cbfe744ed54e5a73efc107c0085ba5777ee93a5f4c1ab90553/torch-2.11.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:63a68fa59de8f87acc7e85a5478bb2dddbb3392b7593ec3e78827c793c4b73fd", size = 419732382, upload-time = "2026-03-23T18:08:30.835Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/e1/b73f7c575a4b8f87a5928f50a1e35416b5e27295d8be9397d5293e7e8d4c/torch-2.11.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:cc89b9b173d9adfab59fd227f0ab5e5516d9a52b658ae41d64e59d2e55a418db", size = 530711509, upload-time = "2026-03-23T18:08:47.213Z" },
+ { url = "https://files.pythonhosted.org/packages/66/82/3e3fcdd388fbe54e29fd3f991f36846ff4ac90b0d0181e9c8f7236565f82/torch-2.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:4dda3b3f52d121063a731ddb835f010dc137b920d7fec2778e52f60d8e4bf0cd", size = 114555842, upload-time = "2026-03-23T18:09:52.111Z" },
+ { url = "https://files.pythonhosted.org/packages/db/38/8ac78069621b8c2b4979c2f96dc8409ef5e9c4189f6aac629189a78677ca/torch-2.11.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8b394322f49af4362d4f80e424bcaca7efcd049619af03a4cf4501520bdf0fb4", size = 80959574, upload-time = "2026-03-23T18:10:14.214Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/6c/56bfb37073e7136e6dd86bfc6af7339946dd684e0ecf2155ac0eee687ae1/torch-2.11.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:2658f34ce7e2dabf4ec73b45e2ca68aedad7a5be87ea756ad656eaf32bf1e1ea", size = 419732324, upload-time = "2026-03-23T18:09:36.604Z" },
+ { url = "https://files.pythonhosted.org/packages/07/f4/1b666b6d61d3394cca306ea543ed03a64aad0a201b6cd159f1d41010aeb1/torch-2.11.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:98bb213c3084cfe176302949bdc360074b18a9da7ab59ef2edc9d9f742504778", size = 530596026, upload-time = "2026-03-23T18:09:20.842Z" },
+ { url = "https://files.pythonhosted.org/packages/48/6b/30d1459fa7e4b67e9e3fe1685ca1d8bb4ce7c62ef436c3a615963c6c866c/torch-2.11.0-cp313-cp313t-win_amd64.whl", hash = "sha256:a97b94bbf62992949b4730c6cd2cc9aee7b335921ee8dc207d930f2ed09ae2db", size = 114793702, upload-time = "2026-03-23T18:09:47.304Z" },
+ { url = "https://files.pythonhosted.org/packages/26/0d/8603382f61abd0db35841148ddc1ffd607bf3100b11c6e1dab6d2fc44e72/torch-2.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:01018087326984a33b64e04c8cb5c2795f9120e0d775ada1f6638840227b04d7", size = 80573442, upload-time = "2026-03-23T18:09:10.117Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/86/7cd7c66cb9cec6be330fff36db5bd0eef386d80c031b581ec81be1d4b26c/torch-2.11.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:2bb3cc54bd0dea126b0060bb1ec9de0f9c7f7342d93d436646516b0330cd5be7", size = 419749385, upload-time = "2026-03-23T18:07:33.77Z" },
+ { url = "https://files.pythonhosted.org/packages/47/e8/b98ca2d39b2e0e4730c0ee52537e488e7008025bc77ca89552ff91021f7c/torch-2.11.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:4dc8b3809469b6c30b411bb8c4cad3828efd26236153d9beb6a3ec500f211a60", size = 530716756, upload-time = "2026-03-23T18:07:50.02Z" },
+ { url = "https://files.pythonhosted.org/packages/78/88/d4a4cda8362f8a30d1ed428564878c3cafb0d87971fbd3947d4c84552095/torch-2.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:2b4e811728bd0cc58fb2b0948fe939a1ee2bf1422f6025be2fca4c7bd9d79718", size = 114552300, upload-time = "2026-03-23T18:09:05.617Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/46/4419098ed6d801750f26567b478fc185c3432e11e2cad712bc6b4c2ab0d0/torch-2.11.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8245477871c3700d4370352ffec94b103cfcb737229445cf9946cddb7b2ca7cd", size = 80959460, upload-time = "2026-03-23T18:09:00.818Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/66/54a56a4a6ceaffb567231994a9745821d3af922a854ed33b0b3a278e0a99/torch-2.11.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:ab9a8482f475f9ba20e12db84b0e55e2f58784bdca43a854a6ccd3fd4b9f75e6", size = 419735835, upload-time = "2026-03-23T18:07:18.974Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/e7/0b6665f533aa9e337662dc190425abc0af1fe3234088f4454c52393ded61/torch-2.11.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:563ed3d25542d7e7bbc5b235ccfacfeb97fb470c7fee257eae599adb8005c8a2", size = 530613405, upload-time = "2026-03-23T18:08:07.014Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/bf/c8d12a2c86dbfd7f40fb2f56fbf5a505ccf2d9ce131eb559dfc7c51e1a04/torch-2.11.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b2a43985ff5ef6ddd923bbcf99943e5f58059805787c5c9a2622bf05ca2965b0", size = 114792991, upload-time = "2026-03-23T18:08:19.216Z" },
]
[[package]]
name = "tqdm"
-version = "4.67.1"
-source = { registry = "https://pypi.org/simple" }
+version = "4.67.3"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737, upload-time = "2024-11-24T20:12:22.481Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/09/a9/6ba95a270c6f1fbcd8dac228323f2777d886cb206987444e4bce66338dd4/tqdm-4.67.3.tar.gz", hash = "sha256:7d825f03f89244ef73f1d4ce193cb1774a8179fd96f31d7e1dcde62092b960bb", size = 169598, upload-time = "2026-02-03T17:35:53.048Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540, upload-time = "2024-11-24T20:12:19.698Z" },
+ { url = "https://files.pythonhosted.org/packages/16/e1/3079a9ff9b8e11b846c6ac5c8b5bfb7ff225eee721825310c91b3b50304f/tqdm-4.67.3-py3-none-any.whl", hash = "sha256:ee1e4c0e59148062281c49d80b25b67771a127c85fc9676d3be5f243206826bf", size = 78374, upload-time = "2026-02-03T17:35:50.982Z" },
]
[[package]]
name = "triton"
-version = "3.5.0"
-source = { registry = "https://pypi.org/simple" }
+version = "3.6.0"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/3d/78/949a04391c21956c816523678f0e5fa308eb5b1e7622d88c4e4ef5fceca0/triton-3.5.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f34bfa21c5b3a203c0f0eab28dcc1e49bd1f67d22724e77fb6665a659200a4ec", size = 170433488, upload-time = "2025-10-13T16:37:57.132Z" },
- { url = "https://files.pythonhosted.org/packages/f5/3a/e991574f3102147b642e49637e0281e9bb7c4ba254edb2bab78247c85e01/triton-3.5.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9e71db82261c4ffa3921cd050cd5faa18322d2d405c30eb56084afaff3b0833", size = 170476535, upload-time = "2025-10-13T16:38:05.18Z" },
- { url = "https://files.pythonhosted.org/packages/6c/29/10728de8a6e932e517c10773486b8e99f85d1b1d9dd87d9a9616e1fef4a1/triton-3.5.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e6bb9aa5519c084a333acdba443789e50012a4b851cd486c54f0b8dc2a8d3a12", size = 170487289, upload-time = "2025-10-13T16:38:11.662Z" },
- { url = "https://files.pythonhosted.org/packages/5c/38/db80e48b9220c9bce872b0f616ad0446cdf554a40b85c7865cbca99ab3c2/triton-3.5.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c83f2343e1a220a716c7b3ab9fccfcbe3ad4020d189549200e2d2e8d5868bed9", size = 170577179, upload-time = "2025-10-13T16:38:17.865Z" },
- { url = "https://files.pythonhosted.org/packages/ff/60/1810655d1d856c9a4fcc90ee8966d85f552d98c53a6589f95ab2cbe27bb8/triton-3.5.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da0fa67ccd76c3dcfb0bffe1b1c57c685136a6bd33d141c24d9655d4185b1289", size = 170487949, upload-time = "2025-10-13T16:38:24.881Z" },
- { url = "https://files.pythonhosted.org/packages/fb/b7/1dec8433ac604c061173d0589d99217fe7bf90a70bdc375e745d044b8aad/triton-3.5.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:317fe477ea8fd4524a6a8c499fb0a36984a56d0b75bf9c9cb6133a1c56d5a6e7", size = 170580176, upload-time = "2025-10-13T16:38:31.14Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/2c/96f92f3c60387e14cc45aed49487f3486f89ea27106c1b1376913c62abe4/triton-3.6.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49df5ef37379c0c2b5c0012286f80174fcf0e073e5ade1ca9a86c36814553651", size = 176081190, upload-time = "2026-01-20T16:16:00.523Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/12/b05ba554d2c623bffa59922b94b0775673de251f468a9609bc9e45de95e9/triton-3.6.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8e323d608e3a9bfcc2d9efcc90ceefb764a82b99dea12a86d643c72539ad5d3", size = 188214640, upload-time = "2026-01-20T16:00:35.869Z" },
+ { url = "https://files.pythonhosted.org/packages/17/5d/08201db32823bdf77a0e2b9039540080b2e5c23a20706ddba942924ebcd6/triton-3.6.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:374f52c11a711fd062b4bfbb201fd9ac0a5febd28a96fb41b4a0f51dde3157f4", size = 176128243, upload-time = "2026-01-20T16:16:07.857Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/a8/cdf8b3e4c98132f965f88c2313a4b493266832ad47fb52f23d14d4f86bb5/triton-3.6.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:74caf5e34b66d9f3a429af689c1c7128daba1d8208df60e81106b115c00d6fca", size = 188266850, upload-time = "2026-01-20T16:00:43.041Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/12/34d71b350e89a204c2c7777a9bba0dcf2f19a5bfdd70b57c4dbc5ffd7154/triton-3.6.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:448e02fe6dc898e9e5aa89cf0ee5c371e99df5aa5e8ad976a80b93334f3494fd", size = 176133521, upload-time = "2026-01-20T16:16:13.321Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/0b/37d991d8c130ce81a8728ae3c25b6e60935838e9be1b58791f5997b24a54/triton-3.6.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:10c7f76c6e72d2ef08df639e3d0d30729112f47a56b0c81672edc05ee5116ac9", size = 188289450, upload-time = "2026-01-20T16:00:49.136Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/4e/41b0c8033b503fd3cfcd12392cdd256945026a91ff02452bef40ec34bee7/triton-3.6.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1722e172d34e32abc3eb7711d0025bb69d7959ebea84e3b7f7a341cd7ed694d6", size = 176276087, upload-time = "2026-01-20T16:16:18.989Z" },
+ { url = "https://files.pythonhosted.org/packages/35/f8/9c66bfc55361ec6d0e4040a0337fb5924ceb23de4648b8a81ae9d33b2b38/triton-3.6.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d002e07d7180fd65e622134fbd980c9a3d4211fb85224b56a0a0efbd422ab72f", size = 188400296, upload-time = "2026-01-20T16:00:56.042Z" },
+ { url = "https://files.pythonhosted.org/packages/49/55/5ecf0dcaa0f2fbbd4420f7ef227ee3cb172e91e5fede9d0ecaddc43363b4/triton-3.6.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef5523241e7d1abca00f1d240949eebdd7c673b005edbbce0aca95b8191f1d43", size = 176138577, upload-time = "2026-01-20T16:16:25.426Z" },
+ { url = "https://files.pythonhosted.org/packages/df/3d/9e7eee57b37c80cec63322c0231bb6da3cfe535a91d7a4d64896fcb89357/triton-3.6.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a17a5d5985f0ac494ed8a8e54568f092f7057ef60e1b0fa09d3fd1512064e803", size = 188273063, upload-time = "2026-01-20T16:01:07.278Z" },
+ { url = "https://files.pythonhosted.org/packages/48/db/56ee649cab5eaff4757541325aca81f52d02d4a7cd3506776cad2451e060/triton-3.6.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0b3a97e8ed304dfa9bd23bb41ca04cdf6b2e617d5e782a8653d616037a5d537d", size = 176274804, upload-time = "2026-01-20T16:16:31.528Z" },
+ { url = "https://files.pythonhosted.org/packages/f6/56/6113c23ff46c00aae423333eb58b3e60bdfe9179d542781955a5e1514cb3/triton-3.6.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:46bd1c1af4b6704e554cad2eeb3b0a6513a980d470ccfa63189737340c7746a7", size = 188397994, upload-time = "2026-01-20T16:01:14.236Z" },
]
[[package]]
name = "typing-extensions"
version = "4.15.0"
-source = { registry = "https://pypi.org/simple" }
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" },
@@ -1710,7 +1831,7 @@ wheels = [
[[package]]
name = "typing-inspection"
version = "0.4.2"
-source = { registry = "https://pypi.org/simple" }
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
dependencies = [
{ name = "typing-extensions" },
]
@@ -1721,11 +1842,11 @@ wheels = [
[[package]]
name = "tzdata"
-version = "2025.3"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/5e/a7/c202b344c5ca7daf398f3b8a477eeb205cf3b6f32e7ec3a6bac0629ca975/tzdata-2025.3.tar.gz", hash = "sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7", size = 196772, upload-time = "2025-12-13T17:45:35.667Z" }
+version = "2026.1"
+source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/19/f5/cd531b2d15a671a40c0f66cf06bc3570a12cd56eef98960068ebbad1bf5a/tzdata-2026.1.tar.gz", hash = "sha256:67658a1903c75917309e753fdc349ac0efd8c27db7a0cb406a25be4840f87f98", size = 197639, upload-time = "2026-04-03T11:25:22.002Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl", hash = "sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1", size = 348521, upload-time = "2025-12-13T17:45:33.889Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/70/d460bd685a170790ec89317e9bd33047988e4bce507b831f5db771e142de/tzdata-2026.1-py2.py3-none-any.whl", hash = "sha256:4b1d2be7ac37ceafd7327b961aa3a54e467efbdb563a23655fbfe0d39cfc42a9", size = 348952, upload-time = "2026-04-03T11:25:20.313Z" },
]
[[package]]
@@ -1741,8 +1862,8 @@ dependencies = [
{ name = "matplotlib" },
{ name = "numpy" },
{ name = "optax" },
- { name = "pandas", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" },
- { name = "pandas", version = "3.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14'" },
+ { name = "pandas", version = "2.3.3", source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }, marker = "python_full_version >= '3.14'" },
+ { name = "pandas", version = "3.0.2", source = { registry = "https://gitlab.college-de-france.fr/api/v4/projects/145/packages/pypi/simple" }, marker = "python_full_version < '3.14'" },
{ name = "scikit-image" },
{ name = "scipy" },
{ name = "tifffile" },
@@ -1750,6 +1871,7 @@ dependencies = [
[package.dev-dependencies]
dev = [
+ { name = "pdoc" },
{ name = "pytest" },
{ name = "scalene" },
]
@@ -1772,6 +1894,7 @@ requires-dist = [
[package.metadata.requires-dev]
dev = [
+ { name = "pdoc", specifier = ">=16.0.0" },
{ name = "pytest", specifier = ">=8.4.2" },
{ name = "scalene", specifier = ">=1.5.55" },
]