1 /* Copyright (c) 1997-2021
2    Ewgenij Gawrilow, Michael Joswig, and the polymake team
3    Technische Universität Berlin, Germany
4    https://polymake.org
5 
6    This program is free software; you can redistribute it and/or modify it
7    under the terms of the GNU General Public License as published by the
8    Free Software Foundation; either version 2, or (at your option) any
9    later version: http://www.gnu.org/licenses/gpl.txt.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15 --------------------------------------------------------------------------------
16 */
17 
18 #include "polymake/client.h"
19 #include "polymake/list"
20 #include "polymake/Array.h"
21 #include "polymake/Set.h"
22 #include "polymake/Bitset.h"
23 #include "polymake/IncidenceMatrix.h"
24 
25 namespace polymake { namespace polytope {
26 namespace {
27 
28 typedef std::list<std::string> label_list;
29 typedef RestrictedIncidenceMatrix<only_cols> facet_matrix;
30 
31 template <typename SetTop>
circuit_label(const GenericSet<SetTop> & circuit)32 std::string circuit_label (const GenericSet<SetTop>& circuit)
33 {
34    std::string label;
35    Int pos = 0;
36    for (auto it = entire(circuit.top()); !it.at_end(); ++it) {
37       const Int e(*it), e2(e/2);
38       label.append(e2-pos, '0');
39       label+= e%2==0? '+' : '-';
40       pos=e2+1;
41    }
42    return label;
43 }
44 
45 template <typename SetTop> inline
add_facet(facet_matrix & VIF,label_list & labels,const GenericSet<SetTop> & circuit,const Array<Bitset> & CubeFacets)46 void add_facet (facet_matrix& VIF, label_list& labels,
47                 const GenericSet<SetTop>& circuit, const Array<Bitset>& CubeFacets)
48 {
49    VIF /= accumulate(select(CubeFacets, circuit), operations::mul());
50    labels.push_back(circuit_label(circuit));
51 }
52 
53 // subset of given order represented by least significant bits
first_valid_subset(const Int order)54 Int first_valid_subset(const Int order) { return (1L << order)-1; }
55 
last_bit_set(const Int x)56 bool last_bit_set(const Int x) { return x&1; }
last_bit_clear(const Int x)57 bool last_bit_clear(const Int x) { return !(x&1); }
last_two_bits_clear(const Int x)58 bool last_two_bits_clear(const Int x) { return !(x&3); }
59 
60 // count the number of bits
card(Int subset)61 Int card(Int subset)
62 {
63    return __builtin_popcountl(static_cast<unsigned long>(subset));
64 }
65 
66 // check for even gaps and set 0-bit
valid(Int subset)67 bool valid(Int subset)
68 {
69    if (!subset) return true;
70    if (last_bit_clear(subset)) return false;
71    for (;;) {
72       while (last_bit_set(subset)) subset/=2;
73       if (!subset) return true;
74       while (last_two_bits_clear(subset)) subset/=4;
75       if (last_bit_clear(subset)) return false;
76    }
77 }
78 
79 // next subset of given order with even gaps (no gap at the beginning)
next_valid_subset(const Int order,Int last)80 Int next_valid_subset(const Int order, Int last)
81 {
82    Int subset(last | 1); // make sure it's odd
83    for (;;) {
84       subset += 2;
85       const Int c = card(subset);
86       if (c != order) continue;
87       if (valid(subset)) return subset;
88    }
89 }
90 
extend_circuits(facet_matrix & VIF,label_list & labels,const Set<Int> & circuit,const Array<Bitset> & CubeFacets,const Int n,const Int d,const Int p,const Int k)91 void extend_circuits(facet_matrix& VIF,
92                      label_list& labels,
93                      const Set<Int>& circuit,
94                      const Array<Bitset>& CubeFacets,
95                      const Int n, const Int d,
96                      const Int p, const Int k)
97 {
98    // enumerate some subsets of {p+k,..,n-1} of order n-d+1-p
99    const Int
100       order = n-d-p+1,
101       o2 = 1L <<order,
102       m = n-p-k, // number of bits necessary to represent subset
103       m2 = 1L << m; // 2**m
104    Int subset = first_valid_subset(order);
105    while (subset < m2) {
106       // variate all signs
107       for (Int sign_vector = 0; sign_vector < o2; ++sign_vector) {
108          Set<Int> S;
109          Int check_signs = 1;
110          for (Int y = 1, no = 0; y <= subset; y *= 2, ++no) {
111             if (y&subset) {
112                if (check_signs&sign_vector)
113                   S += 2*(p+k+no)+1;
114                else
115                   S += 2*(p+k+no);
116                check_signs*=2;
117             }
118          }
119          add_facet(VIF, labels, circuit+S, CubeFacets);
120       }
121       if (order==1) break;
122       subset = next_valid_subset(order,subset);
123    }
124 }
125 
126 } // end unnamed namespace
127 
neighborly_cubical(Int d,Int n)128 BigObject neighborly_cubical(Int d, Int n)
129 {
130   // there is a certain restriction on the parameters
131   const Int m = 8*sizeof(Int)-2;
132   if (d < 2 || d > n || n > m)
133     throw std::runtime_error("neighborly_cubical: 2 <= d <= n <= " + std::to_string(m));
134 
135   const Int n_vertices = 1L << n;
136 
137   // produce a combinatorial description of the n-cube
138    Array<Bitset> CubeFacets(2*n);
139    for (Int i = 0; i < n_vertices; ++i)
140      for (Int x = i, k = 0; k < n; x >>= 1, ++k)
141        CubeFacets[2*k+(x%2)] += i;
142 
143    facet_matrix VIF(n_vertices);
144    label_list labels;
145 
146    Set<Int> circuit; // the one to extend
147 
148    // the "p==0" case: Gale's evenness criterion
149    for (Int k = 1; k < d; ++k)
150       extend_circuits(VIF,labels,circuit,CubeFacets,n,d,0,k);
151 
152    // take the first p but not the (p+1)st
153    for (Int p = 1; p <= n-d; ++p) {
154       const Int pm1 = p-1;
155 
156       // the signs among the first p-1 vectors alternate, starting with -1
157       Set<Int> c_pm1(series(1,pm1-pm1/2,4) + series(2,pm1/2,4));
158 
159       // consider an even gap; then the sign sigma at p is (-1)^p
160       if (last_bit_clear(p)) // p even
161          circuit = c_pm1 + (2*pm1);   // "+"
162       else
163          circuit = c_pm1 + (2*pm1+1); // "-"
164 
165       // just a partial circuit up to now
166       for (Int k = 2; p+k < n; k += 2) {
167          extend_circuits(VIF,labels,circuit,CubeFacets,n,d,p,k);
168       }
169 
170       // consider an odd gap; then the sign sigma at p is (-1)^p
171       if (last_bit_clear(p)) // p even
172          circuit = c_pm1 + (2*pm1+1); // "-"
173       else
174          circuit = c_pm1 + (2*pm1);   // "+"
175       // just a partial circuit up to now
176       for (Int k = 1; p+k < n; k += 2) {
177          extend_circuits(VIF,labels,circuit,CubeFacets,n,d,p,k);
178       }
179    }
180 
181    // now we need the circuits consisting of the first n-d+1 (two choices for signs)
182    const Int pm1 = n-d;
183    circuit = series(1,pm1-pm1/2,4) + series(2,pm1/2,4); // always starts with a negative one
184    add_facet(VIF, labels, circuit+(2*pm1), CubeFacets);
185    add_facet(VIF, labels, circuit+(2*pm1+1), CubeFacets);
186 
187    BigObject p_out("Polytope<Rational>",
188                    "VERTICES_IN_FACETS", IncidenceMatrix<>(std::move(VIF)),
189                    "N_VERTICES", n_vertices,
190                    "FACET_LABELS", labels);
191    p_out.set_description() << "Neighborly cubical " << d << "-polytope" << endl;
192    return p_out;
193 }
194 
195 UserFunction4perl("# @category Producing a polytope from scratch"
196                   "# Produce the combinatorial description of a neighborly cubical polytope."
197                   "# The facets are labelled in oriented matroid notation as in the cubical Gale evenness criterion."
198                   "#\t See Joswig and Ziegler, Discr. Comput. Geom. 24:315-344 (2000)."
199                   "# @param Int d dimension of the polytope"
200                   "# @param Int n dimension of the equivalent cube"
201                   "# @return Polytope",
202                   &neighborly_cubical, "neighborly_cubical");
203 } }
204 
205 // Local Variables:
206 // mode:C++
207 // c-basic-offset:3
208 // indent-tabs-mode:nil
209 // End:
210