diff --git a/Project.toml b/Project.toml index 130e43c2..5bdcc209 100644 --- a/Project.toml +++ b/Project.toml @@ -11,6 +11,8 @@ JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" LoadFlint = "472f376f-f1cf-461b-9ac1-d103423be9b7" Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a" +Mongoc = "4fe8b98c-fc19-5c23-8ec2-168ff83495f2" +MozillaCACerts_jll = "14a3606d-f60d-562e-9121-12d972cd8159" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" diff --git a/deps/rules/julia.rules b/deps/rules/julia.rules index 45fa0f37..ce2568eb 100644 --- a/deps/rules/julia.rules +++ b/deps/rules/julia.rules @@ -66,3 +66,9 @@ function jupyter_visual_svg(Visual::Object+) { close $handle; return $string; } + +function deserialize_json_string($) { + my ($str) = @_; + my $hash = decode_json($str); + return Core::Serializer::deserialize($hash); +} diff --git a/examples/polydb_julia.ipynb b/examples/polydb_julia.ipynb new file mode 100644 index 00000000..a2f1a5dc --- /dev/null +++ b/examples/polydb_julia.ipynb @@ -0,0 +1,718 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# PolyDB and Julia" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Getting Started" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this guide we will see how accessing the PolyDB using Polymake.jl works. We do this by following the example from the guide \"polymake file format and the polyDB\" as closely as possible." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "https://polymake.org/doku.php/user_guide/tutorials/json_and_polydb" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "First we start Polymake.jl:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "scrolled": true, + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "polymake version 4.0\n", + "Copyright (c) 1997-2020\n", + "Ewgenij Gawrilow, Michael Joswig, and the polymake team\n", + "Technische Universität Berlin, Germany\n", + "https://polymake.org\n", + "\n", + "This is free software licensed under GPL; see the source for copying conditions.\n", + "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n", + "\n" + ] + } + ], + "source": [ + "using Polymake" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can now connect to the PolyDB with the function `Polymake.Polydb.get_db()`" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Polymake.Polydb.Database(Database(Client(URI(\"mongodb://polymake:database@db.polymake.org/?authSource=admin&ssl=true&sslCertificateAuthorityFile=/home/jordan/.julia/artifacts/59cb7446ab8874d1c1c4df2eee3cba7c76ed4d1d/share/cacert.pem\")), \"polydb\"))" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pdb_default = Polymake.Polydb.get_db()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Info" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "SECTION: Tropical\n", + "\tThis database contains tropical objects.\n", + "\n", + "\tCOLLECTION: Tropical.TOM\n", + "\tAll known non-realisable tropical oriented matroids with parameters n=6, d=3 or n=d=4.\n", + "\n", + "\tCOLLECTION: Tropical.Polytropes\n", + "\tRepresentatives for all combinatorial tropical types of full-dimensional polytropes in TP3.\n", + "\n", + "\tCOLLECTION: Tropical.Cubics\n", + "\tRegular unimodular triangulations of the triple tetrahedron and their motifs.\n", + "\n", + "\tCOLLECTION: Tropical.SchlaefliFan\n", + "\tRegular unimodular triangulations of the triple tetrahedron and their motifs.\n", + "\n", + "SECTION: Polytopes\n", + "\tA collection of families of polytopes\n", + "\n", + "SECTION: Polytopes.Combinatorial\n", + "\tThis database contains various classes of combinatorial polytopes.\n", + "\n", + "\tCOLLECTION: Polytopes.Combinatorial.SmallSpheresDim4\n", + "\tCombinatorial 3-spheres with up to 9 vertices\n", + "\n", + "\tCOLLECTION: Polytopes.Combinatorial.FacesBirkhoffPolytope\n", + "\tCombinatorial types of faces up to dimension 8 of a Birkhoff polytope in any dimension\n", + "\n", + "\tCOLLECTION: Polytopes.Combinatorial.01Polytopes\n", + "\t0/1-Polytopes up to combinatorial equivalence\n", + "\n", + "\tCOLLECTION: Polytopes.Combinatorial.CombinatorialTypes\n", + "\tCombinatorial types of polytopes of small dimension and number of vertices\n", + "\n", + "SECTION: Polytopes.Lattice\n", + "\tThis database contains various classes of lattice polytopes.\n", + "\n", + "\tCOLLECTION: Polytopes.Lattice.Reflexive\n", + "\tReflexive Polytopes in dimension 4\n", + "\n", + "\tCOLLECTION: Polytopes.Lattice.Panoptigons\n", + "\tNon-hyperelliptic Panoptigons of lattice diameter at least 3\n", + "\n", + "\tCOLLECTION: Polytopes.Lattice.01Polytopes\n", + "\t0/1-Polytopes up to lattice equivalence\n", + "\n", + "\tCOLLECTION: Polytopes.Lattice.ExceptionalMaximalHollow\n", + "\tExceptional maximal hollow lattice polytopes up to dimension 3\n", + "\n", + "\tCOLLECTION: Polytopes.Lattice.SmoothReflexive\n", + "\tSmooth reflexive polytopes in dimensions 1 to 9 with splitting data\n", + "\n", + "SECTION: Polytopes.Geometric\n", + "\tThis database contains various classes of geometric polytopes.\n", + "\n", + "\tCOLLECTION: Polytopes.Geometric.01Polytopes\n", + "\t0/1-Polytopes up to rotation and reflection\n", + "\n", + "SECTION: Matroids\n", + "\tThis database contains various classes of matroids.\n", + "\n", + "\tCOLLECTION: Matroids.Small\n", + "\tCollection of small matroids based on the census by Yoshitake Matsumoto, Sonoko Moriyama, Hiroshi Imai, David Bremner.\n", + "\n", + "SECTION: Manifolds\n", + "\tThis database contains combinatorial manifolds\n", + "\n", + "\tCOLLECTION: Manifolds.DIM2_3\n", + "\tThis is a collection of combinatorial 2- and 3-manifolds with up to 10 vertices given as triangulations and calculcated by Frank Lutz found at: http://page.math.tu-berlin.de/~lutz/stellar/mixed.html.\n" + ] + } + ], + "source": [ + "Polymake.Polydb.info(pdb_default)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For the database individual collections we can get more detailed information:\n", + " * level 1: short description (default if database is given),\n", + " * level 2: description, \n", + " * level 3: description, authors, maintainers, \n", + " * level 4: full info\n", + " * level 5: full info and list of recommended search fields, if defined in info (default if collection is given)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "SECTION: Tropical\n", + "This database contains tropical objects.\n", + "\n", + "\tCOLLECTION: Tropical.TOM\n", + "\tAll known non-realisable tropical oriented matroids with parameters n=6, d=3 or n=d=4. They were computed using polymake with the tropmat extension and Topcom. You need the extension tropmat for this.\n", + "\n", + "\tCOLLECTION: Tropical.Polytropes\n", + "\tRepresentatives for all combinatorial tropical types of full-dimensional polytropes in TP3, computed by Ngoc Tran, see [[arXiv:1310.2012]] and [[https://github.com/princengoc/polytropes]].\n", + "\n", + "\tCOLLECTION: Tropical.Cubics\n", + "\tRegular unimodular triangulations of the triple tetrahedron and their motifs. The latter comprise additional data relevant for identifying tropical lines in the dual tropical cubic surfaces. This database is explained and used in the article 'The Schläfli fan' by Michael Joswig, Marta Panizzut and Bernd Sturmfels. For best way of using the [[https://polymake.org/extensions/tropicalcubics|polymake extension TropicalCubics]] is recommended.\n", + "\n", + "\tCOLLECTION: Tropical.SchlaefliFan\n", + "\tRegular unimodular triangulations of the triple tetrahedron and their motifs. The latter comprise additional data relevant for identifying tropical lines in the dual tropical cubic surfaces. This database is explained and used in the article 'The Schläfli fan' by Michael Joswig, Marta Panizzut and Bernd Sturmfels. For best way of using the [[https://polymake.org/extensions/tropicalcubics|polymake extension TropicalCubics]] is recommended.\n", + "\n", + "SECTION: Polytopes\n", + "A collection of families of polytopes\n", + "\n", + "SECTION: Polytopes.Combinatorial\n", + "This database contains various classes of combinatorial polytopes.\n", + "\n", + "\tCOLLECTION: Polytopes.Combinatorial.SmallSpheresDim4\n", + "\tAll combinatorial 3-spheres with up to 9 vertices together with information about their realizability. For up to seven vertices this was obtained by Perles (see [Gr?nbaum 1967]), for eight vertices by Altshuler and Steinberg, and for nine vertices by Firsching. For all entries the database contains the incidence matrix (VERTICES_IN_FACETS) information about realizability (REALIZABLE), the f-vector (F_VECTOR), the number of vertices and facets (N_VERTICES, N_FACETS), and the information whether the sphere is simple of simplicial (SIMPLE, SIMPLICIAL). For realizable spheres with nine vertices (i.e. 4-polytopes with nine vertices) there is also a list of vertices (VERTICES), while for the non-realizable spheres with nine vertices there is also a proof of non-realizability that depends on a possibly incomplete computation of the chirotope (PARTIAL_CHIROTOPE). This proof can be of one of three types. The first two depend on the Grassmann-Pl?cker relations. In the first type the chirotope contains enough information to find a violated relation, and the three involved terms are given. In the second type two relations are given that would allow to deduce a not yet known entry of the chirotope, but do not infer the same value. In the last type there is a complete chirotope, but the linear program to the biquadratic final polynomial is infeasible, and the linear program is given. For a detailed explanation of the proofs see the paper of Firsching.\n", + "\n", + "\tCOLLECTION: Polytopes.Combinatorial.FacesBirkhoffPolytope\n", + "\tCombinatorial types of faces up to dimension 8 of a Birkhoff polytope in any dimension.\n", + "\n", + "\tCOLLECTION: Polytopes.Combinatorial.01Polytopes\n", + "\t0/1-Polytopes up to combinatorial equivalence\n", + "\n", + "\tCOLLECTION: Polytopes.Combinatorial.CombinatorialTypes\n", + "\tCombinatorial types of polytopes of small dimension and number of vertices, see [[http://www-imai.is.s.u-tokyo.ac.jp/~hmiyata/oriented_matroids/index.html]]\n", + "\n", + "SECTION: Polytopes.Lattice\n", + "This database contains various classes of lattice polytopes.\n", + "\n", + "\tCOLLECTION: Polytopes.Lattice.Reflexive\n", + "\tReflexive Polytopes in dimension 4\n", + "\n", + "\tCOLLECTION: Polytopes.Lattice.Panoptigons\n", + "\tNon-hyperelliptic Panoptigons of lattice diameter at least 3\n", + "\n", + "\tCOLLECTION: Polytopes.Lattice.01Polytopes\n", + "\t0/1-Polytopes up to lattice equivalence\n", + "\n", + "\tCOLLECTION: Polytopes.Lattice.ExceptionalMaximalHollow\n", + "\tThe finite list of exceptional maximal hollow lattice polytopes up to dimension 3. Any other hollow lattice polytope either is contained in one of the exceptional ones or projects to a lower dimensional hollow lattice polytope\n", + "\n", + "\tCOLLECTION: Polytopes.Lattice.SmoothReflexive\n", + "\tSmooth reflexive lattice polytopes in dimensions up to 9, up to lattice equivalence. The lists were computed with the algorithm of Mikkel Oebro (see [[http://arxiv.org/abs/0704.0049|arxiv: 0704.0049]]) and are taken from the [[http://polymake.org/polytopes/paffenholz/www/fano.html|website of Andreas Paffenholz]]. They also contain splitting data according to [[https://arxiv.org/abs/1711.02936| arxiv: 1711.02936]].\n", + "\n", + "SECTION: Polytopes.Geometric\n", + "This database contains various classes of geometric polytopes.\n", + "\n", + "\tCOLLECTION: Polytopes.Geometric.01Polytopes\n", + "\t0/1-Polytopes up to rotation and reflection\n", + "\n", + "SECTION: Matroids\n", + "This database contains various classes of matroids.\n", + "\n", + "\tCOLLECTION: Matroids.Small\n", + "\tThis is a collection of small matroids based on the census by Yoshitake Matsumoto, Sonoko Moriyama, Hiroshi Imai, David Bremner found at: http://www-imai.is.s.u-tokyo.ac.jp/~ymatsu/matroid/index.html It contains a representative for each isomorphism class of a matroid on at most 12 elements and rank at most half the size of the ground set (note that each matroid contains a DUAL object). It is missing matroids for (n,r) = (10,4), (10,5), (11,4), (11,5), (12,4), (12,5), (12,6). The list for (10,4) is actually part of the above-mentioned census, but was too large to compute all properties.\n", + "\n", + "SECTION: Manifolds\n", + "This database contains combinatorial manifolds\n", + "\n", + "\tCOLLECTION: Manifolds.DIM2_3\n", + "\tThis is a collection of combinatorial 2- and 3-manifolds with up to 10 vertices given as triangulations and calculcated by Frank Lutz found at: http://page.math.tu-berlin.de/~lutz/stellar/mixed.html.\n" + ] + } + ], + "source": [ + "Polymake.Polydb.info(pdb_default, 2)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "SECTION: Polytopes\n", + "A collection of families of polytopes\n", + "\n", + "SECTION: Polytopes.Combinatorial\n", + "This database contains various classes of combinatorial polytopes.\n", + "Maintained by Andreas Paffenholz, paffenholz@opt.tu-darmstadt.de, TU Darmstadt\n", + "\n", + "\tCOLLECTION: Polytopes.Combinatorial.CombinatorialTypes\n", + "\tCombinatorial types of polytopes of small dimension and number of vertices, see [[http://www-imai.is.s.u-tokyo.ac.jp/~hmiyata/oriented_matroids/index.html]]\n", + "\tAuthored by \n", + "\t\tHiroyuki Miyata, hmiyata@is.s.u-tokyo.ac.jp, http://www-imai.is.s.u-tokyo.ac.jp/~hmiyata/, University of Tokyo, Bunkyo-ku, Tokyo, Japan\n", + "\t\tSonoko Moriyama, moriso@dais.is.tohoku.ac.jp , http://www.dais.is.tohoku.ac.jp/~moriso/, Tokuyama laboratory, System Information Sciences, Graduate School of Information Sciences, Tohoku University\n", + "\t\tKomei Fukuda, fukuda@math.ethz.ch, https://www.inf.ethz.ch/personal/fukudak/, Theory of Combinatorial Algorithms, Department of Mathematics, ETHZ\n", + "\tMaintained by\n", + "\t\tConstantin Fischer, cfischer@mailbox.tu-berlin.de, TU Berlin\n" + ] + } + ], + "source": [ + "coll_default = pdb_default[\"Polytopes.Combinatorial.CombinatorialTypes\"]\n", + "Polymake.Polydb.info(coll_default, 4)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Reading data from Julia" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Simple reading" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Polymake.Polydb.Collection{Polymake.BigObject}: Polytopes.Lattice.SmoothReflexive" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "coll_default = pdb_default[\"Polytopes.Lattice.SmoothReflexive\"]" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Polymake.Polydb.Cursor{Polymake.BigObject}(Mongoc.Cursor{Mongoc.Collection}(Collection(Database(Client(URI(\"mongodb://polymake:database@db.polymake.org/?authSource=admin&ssl=true&sslCertificateAuthorityFile=/home/jordan/.julia/artifacts/59cb7446ab8874d1c1c4df2eee3cba7c76ed4d1d/share/cacert.pem\")), \"polydb\"), \"Polytopes.Lattice.SmoothReflexive\"), Ptr{Nothing} @0x000000000b657940))" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a = Polymake.Polydb.find(coll_default, \"DIM\" => 3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This returns a cursor over the data. You can use the standard julia iterator ways for accessing it:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "pm::Vector\n", + "8 12 6\n", + "pm::Vector\n", + "6 9 5\n", + "pm::Vector\n", + "10 15 7\n", + "pm::Vector\n", + "12 18 8\n", + "pm::Vector\n", + "10 15 7\n", + "pm::Vector\n", + "8 12 6\n", + "pm::Vector\n", + "8 12 6\n", + "pm::Vector\n", + "10 15 7\n", + "pm::Vector\n", + "10 15 7\n", + "pm::Vector\n", + "12 18 8\n", + "pm::Vector\n", + "8 12 6\n", + "pm::Vector\n", + "8 12 6\n", + "pm::Vector\n", + "8 12 6\n", + "pm::Vector\n", + "6 9 5\n", + "pm::Vector\n", + "6 9 5\n", + "pm::Vector\n", + "8 12 6\n", + "pm::Vector\n", + "6 9 5\n", + "pm::Vector\n", + "4 6 4\n" + ] + } + ], + "source": [ + "for p in a\n", + " println(p.F_VECTOR)\n", + "end" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And for single element steps:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "pm::Vector\n", + "8 12 6" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a = Polymake.Polydb.find(coll_default, \"DIM\" => 3)\n", + "p = iterate(a)[1]\n", + "p.F_VECTOR" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "pm::Vector\n", + "6 9 5" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "p = iterate(a)[1]\n", + "p.F_VECTOR" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Queries" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Queries can be written as either a `Dict` or a collection of `Pair`s;\n", + "both function calls of `Polymake.Polydb.find` below are equivalent to each other:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Polymake.Polydb.Cursor{Polymake.BigObject}(Mongoc.Cursor{Mongoc.Collection}(Collection(Database(Client(URI(\"mongodb://polymake:database@db.polymake.org/?authSource=admin&ssl=true&sslCertificateAuthorityFile=/home/jordan/.julia/artifacts/59cb7446ab8874d1c1c4df2eee3cba7c76ed4d1d/share/cacert.pem\")), \"polydb\"), \"Polytopes.Lattice.SmoothReflexive\"), Ptr{Nothing} @0x000000000bd65610))" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "query = Dict(\"DIM\"=>3, \"N_VERTICES\" => 8)\n", + "cur = Polymake.Polydb.find(coll_default, query)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A query usually returns a cursor over all matching results." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3, 8\n", + "3, 8\n", + "3, 8\n", + "3, 8\n", + "3, 8\n", + "3, 8\n", + "3, 8\n" + ] + } + ], + "source": [ + "for p in cur\n", + " println(string(polytope.dim(p), \", \", p.N_VERTICES))\n", + "end" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "The cursor keeps its position, so to return to the first result we need to get a new cursor from the same query:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3, 8\n", + "3, 8\n", + "3, 8\n", + "3, 8\n", + "3, 8\n", + "3, 8\n", + "3, 8\n" + ] + } + ], + "source": [ + "cur = Polymake.Polydb.find(coll_default, \"DIM\"=>3, \"N_VERTICES\" => 8)\n", + "for p in cur\n", + " println(string(polytope.dim(p), \", \", p.N_VERTICES))\n", + "end" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "We can collect all results into an `Array`:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "7" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "cur = Polymake.Polydb.find(coll_default, \"DIM\"=>3, \"N_VERTICES\" => 8)\n", + "a = collect(cur)\n", + "length(a)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Type matters:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2\n" + ] + } + ], + "source": [ + "a = Polymake.Polydb.find(coll_default, \"LATTICE_VOLUME\" => \"8\")\n", + "println(length(collect(a)))" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0\n" + ] + } + ], + "source": [ + "a = Polymake.Polydb.find(coll_default, \"LATTICE_VOLUME\" => 8)\n", + "println(length(collect(a)))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "celltoolbar": "Slideshow", + "kernelspec": { + "display_name": "Julia 1.4.1", + "language": "julia", + "name": "julia-1.4" + }, + "language_info": { + "file_extension": ".jl", + "mimetype": "application/julia", + "name": "julia", + "version": "1.4.1" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/src/Polymake.jl b/src/Polymake.jl index 9ba60f1b..2f5cd165 100644 --- a/src/Polymake.jl +++ b/src/Polymake.jl @@ -144,4 +144,6 @@ include("polynomial.jl") include("polymake_direct_calls.jl") include("generate_applications.jl") + +include("polydb.jl") end # of module Polymake diff --git a/src/polydb.jl b/src/polydb.jl new file mode 100644 index 00000000..58da9b24 --- /dev/null +++ b/src/polydb.jl @@ -0,0 +1,295 @@ +module Polydb + +import Polymake: call_function + +using Polymake + +# julia itself also has a cert.pem but this one should be more recent +# and provides a variable for the path +using MozillaCACerts_jll + +using Mongoc + +import Mongoc: find + +#Polymake.Polydb's types store information via +# a corresponding Mongoc type variable +struct Database + mdb::Mongoc.Database +end + +struct Collection{T} + mcol::Mongoc.Collection +end + +struct Cursor{T} + mcursor::Mongoc.Cursor{Mongoc.Collection} +end + +# connects to the Polydb and +# returns a Polymake.Polydb.Database instance +function get_db() + # we explicitly set the cacert file, otherwise we might get connection errors because the certificate cannot be validated + client = Mongoc.Client("mongodb://polymake:database@db.polymake.org/?authSource=admin&ssl=true&sslCertificateAuthorityFile=$(cacert)") + return Database(client["polydb"]) +end + +# returns a Polymake.Polydb.Collection instance with the given name +# sections and collections in the name are connected with the '.' sign, +# i.e. names = "Polytopes.Lattice.SmoothReflexive" +Base.getindex(db::Database, name::AbstractString) = Collection{Polymake.BigObject}(db.mdb[name]) + +# search a collection for documents matching the criteria given by d +function Mongoc.find(c::Collection{T}, d::Dict=Dict(); opts::Union{Nothing, Dict}=nothing) where T + return Cursor{T}(Mongoc.find(c.mcol, Mongoc.BSON(d); options=opts)) +end + +function Mongoc.find(c::Collection{T}, d::Pair...) where T + return Cursor{T}(Mongoc.find(c.mcol, Mongoc.BSON(d...))) +end + +# creating `BSON` iterators from the respective `Polymake.BigObject` iterator +function Collection{Mongoc.BSON}(c::Collection{Polymake.BigObject}) + return Collection{Mongoc.BSON}(c.mcol) +end + +function Cursor{Mongoc.BSON}(cursor::Cursor{Polymake.BigObject}) + return Cursor{Mongoc.BSON}(cursor.mcursor) +end + +# returns a Polymake.BigObject from a Mongoc.BSON document +function parse_document(bson::Mongoc.BSON) + str = Mongoc.as_json(bson) + return @pm common.deserialize_json_string(str) +end + +#Iterator + +Base.IteratorSize(::Type{<:Cursor}) = Base.SizeUnknown() +Base.eltype(::Cursor{T}) where T = T + +# default iteration functions returning `Polymake.BigObject`s +function Base.iterate(cursor::Polymake.Polydb.Cursor{Polymake.BigObject}, state::Nothing=nothing) + next = iterate(cursor.mcursor, state) + isnothing(next) && return nothing + return Polymake.Polydb.parse_document(first(next)), nothing +end + +Base.iterate(coll::Polymake.Polydb.Collection{T}) where T = + return iterate(coll, Cursor{T}(find(coll))) + +function Base.iterate(coll::Polymake.Polydb.Collection, state::Polymake.Polydb.Cursor) + next = iterate(state, nothing) + isnothing(next) && return nothing + doc, _ = next + return doc, state +end + +# functions for `BSON` iteration +Base.iterate(cursor::Cursor{Mongoc.BSON}, state::Nothing=nothing) = + iterate(cursor.mcursor, state) + +Base.iterate(coll::Collection{Mongoc.BSON}) = + iterate(coll.mcol) + +Base.iterate(coll::Collection{Mongoc.BSON}, state::Mongoc.Cursor) = + iterate(coll.mcol, state) + +#Info + +# returns an `Array{String, 1}` of the fields of a collection + +function get_fields(coll::Collection) + db = coll.mcol.database + coll_c = db[string("_collectionInfo.", coll.mcol.name)] + info1 = Mongoc.find_one(coll_c, Mongoc.BSON("_id" => "info.2.1")) + info2 = Mongoc.find_one(coll_c, Mongoc.BSON("_id" => info1["schema"])) + schema = info2["schema"] + temp = Array{String, 1}() + if haskey(schema, "required") + temp = Array{String, 1}(schema["required"]) + else + temp = _read_fields(schema) + end + return temp[(!startswith).(temp, "_")] +end + +function _get_field_string(coll::Collection) + return join(get_fields(coll), ", ") +end + +# recursive helpers to read more complex metadata +# currently only neccessary for `Polytopes.Lattice.SmoothReflexive` +function _read_fields(a::Array) + res = Array{String, 1}() + for entry in a + append!(res, _read_fields(entry)) + end + return res +end + +function _read_fields(d::Dict) + if haskey(d, "required") + return d["required"] + elseif haskey(d, "then") + return _read_fields(d["then"]) + else + return _read_fields(d["allOf"]) + end +end + +# shows information about a specific Collection +function Base.show(io::IO, coll::Collection) + db = Database(coll.mcol.database) + print(io, typeof(coll), "\n", _get_collection_string(db, coll.mcol.name, 5)) +end + +Base.show(io::IO, ::MIME"text/plain", coll::Collection) = print(io, typeof(coll), ": ", coll.mcol.name) + +# returns an array containing the names of all collections in the Polydb, also including meta collections +function _get_collection_names(db::Database) + opts = Mongoc.BSON("authorizedCollections" => true, "nameOnly" => true) + return Mongoc.get_collection_names(db.mdb;options=opts) +end + +# returns an array cotaining the names of all collections in the Polydb, excluding meta collections +function get_collection_names(db::Database) + names = _get_collection_names(db) + res = Array{String, 1}() + sizehint!(res, floor(Int, length(names)/2)) + for name in names + if !startswith(name, "_") + push!(res, name) + end + end + return res +end + +# functions helping printing metadata for sections or collections +function _get_contact(s::String) + return s +end + +function _get_contact(a::Array) + res = Array{String, 1}() + for dict in a + str = Array{String, 1}() + for key in ["name", "email", "www", "affiliation"] + if !isempty(get(dict, key, "")) + push!(str, dict[key]) + end + end + push!(res, join(str, ", ")) + end + return string("\t\t", join(res, "\n\t\t")) +end + +# returns information String about a specific section +function _get_section_string(db::Database, name::String, level::Base.Integer) + info = _get_info_document(db, string("_sectionInfo.", name)) + res = [string("SECTION: ", join(info["section"], "."))] + if level == 1 && haskey(info, "short_description") + push!(res, string("\t", info["short_description"])) + end + if level >= 2 && haskey(info, "description") + push!(res, info["description"]) + end + if level >= 3 && haskey(info, "maintainer") + push!(res, string("Maintained by ", info["maintainer"]["name"], ", ", info["maintainer"]["email"], ", ", info["maintainer"]["affiliation"])) + end + return join(res, "\n") +end + +# returns information String about a specific collection +function _get_collection_string(db::Database, name::String, level::Base.Integer) + info = _get_info_document(db, string("_collectionInfo.", name)) + res = [string("\tCOLLECTION: ", name)] + if level == 1 && haskey(info, "short_description") + push!(res, string("\t", info["short_description"])) + end + if level >= 2 && haskey(info, "description") + push!(res, string("\t", info["description"])) + end + if level >= 3 && haskey(info, "author") + push!(res, string("\tAuthored by ", "\n", _get_contact(info["author"]))) + end + if level >= 3 && haskey(info, "maintainer") + push!(res, string("\tMaintained by", "\n", _get_contact(info["maintainer"]))) + end + if level >= 5 + push!(res, string("\tFields: ", _get_field_string(db[name]))) + end + return join(res, "\n") +end + +# prints a sorted list of the sections and collections of the Polydb +# together with information about each of these, if existent +# relying on the structure of Polydb +function info(db::Database, level::Base.Integer=1) + dbtree = _get_db_tree(db) + println(join(_get_info_strings(db, dbtree, level), "\n\n")) +end + +# returns a tree-like nesting of Dicts and Array{String}s +# representing polyDB's structure +function _get_db_tree(db) + root = Dict{String, Union{Dict, Array{String, 1}}}() + cnames = get_collection_names(db) + for name in cnames + path = split(name, ".") + temp = root + for i=1:length(path)-2 + if !haskey(temp, path[i]) + temp[path[i]] = Dict{String, Union{Dict, Array{String, 1}}}() + end + temp = temp[path[i]] + end + if !haskey(temp, path[length(path)-1]) + temp[path[length(path)-1]] = Array{String, 1}() + end + temp = temp[path[length(path)-1]] + push!(temp, path[end]) + end + return root +end + +# recursively generates the info Strings from the tree received by `_get_db_tree` +function _get_info_strings(db::Database, tree::Dict, level::Base.Integer, path::String="") + res = Array{String, 1}() + for (key, value) in tree + new_path = path == "" ? key : string(path, ".", key) + push!(res, _get_section_string(db, new_path, level)) + append!(res, _get_info_strings(db, value, level, new_path)) + end + return res +end + +# leaves of the tree are the collections, whose names are stored in an Array{String} +function _get_info_strings(db:: Database, colls::Array{String, 1}, level::Base.Integer, path::String="") + res = Array{String, 1}() + for coll in colls + push!(res, _get_collection_string(db, string(path, ".", coll), level)) + end + return res +end + +# for a given collection or section name, +# returns the `BSON` document we read the meta information from +function _get_info_document(db::Database, name::String) + i = startswith(name, "_c") ? 17 : 14 + return Mongoc.find_one(db.mdb[name], Mongoc.BSON("_id" => string(SubString(name, i), ".2.1"))) +end + +function info(coll::Collection, level::Base.Integer=5) + db = Database(coll.mcol.database) + name = coll.mcol.name + parts = split(name, ".") + res = Array{String, 1}() + for (i, section) in enumerate(parts[1:length(parts) - 1]) + push!(res, _get_section_string(db, join(parts[1:i], "."), level)) + end + push!(res, _get_collection_string(db, coll.mcol.name, level)) + println(join(res, "\n\n")) +end + +end