diff --git a/Makefile b/Makefile index 402a664..6be5ece 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,8 @@ CXXFLAGS = $(OPTFLAGS) -Wall -Wno-unused $(INCLUDES) LIB_OBJS = lib/refcount.o \ lib/lib.o lib/smallbitset.o lib/bitset.o lib/setcommon.o lib/io.o lib/directed_multigraph.o ALGEBRA_OBJS = algebra/algebra.o algebra/grading.o algebra/polynomial.o -KNOTKIT_OBJS = planar_diagram.o dt_code.o knot_diagram.o cube.o spanning_tree_complex.o \ +KNOTKIT_OBJS = planar_diagram.o dt_code.o knot_diagram.o cube.o steenrod_square.o \ + spanning_tree_complex.o \ smoothing.o cobordism.o knot_tables.o sseq.o \ knot_parser/knot_parser.o knot_parser/knot_scanner.o \ rd_parser/rd_parser.o rd_parser/rd_scanner.o @@ -38,7 +39,8 @@ ALGEBRA_HEADERS = algebra/algebra.h algebra/grading.h algebra/module.h \ algebra/polynomial.h algebra/multivariate_polynomial.h \ algebra/multivariate_laurentpoly.h algebra/fraction_field.h KNOTKIT_HEADERS = knotkit.h planar_diagram.h dt_code.h knot_diagram.h \ - smoothing.h cobordism.h cube.h spanning_tree_complex.h cube_impl.h sseq.h + smoothing.h cobordism.h cube.h steenrod_square.h \ + spanning_tree_complex.h cube_impl.h sseq.h simplify_chain_complex.h LIBS = -lgmp -lz diff --git a/algebra/Z2.h b/algebra/Z2.h index 0cb8799..3765fb9 100644 --- a/algebra/Z2.h +++ b/algebra/Z2.h @@ -11,6 +11,7 @@ class Z2 public: Z2 () : v(0) { } Z2 (int x) : v((bool)(x & 1)) { } + Z2 (unsigned x) : v((bool)(x & 1)) { } Z2 (bool v_) : v(v_) { } Z2 (const Z2 &x) : v(x.v) { } Z2 (copy, const Z2 &x) : v(x.v) { } diff --git a/algebra/linear_combination.h b/algebra/linear_combination.h index c3d6bba..a53727a 100644 --- a/algebra/linear_combination.h +++ b/algebra/linear_combination.h @@ -40,7 +40,7 @@ class linear_combination v.push (i.key (), R (COPY, i.val ())); } ~linear_combination () { } - + linear_combination &operator = (const linear_combination &lc) { m = lc.m; diff --git a/algebra/module.h b/algebra/module.h index 71f3eff..63cdb87 100644 --- a/algebra/module.h +++ b/algebra/module.h @@ -628,7 +628,8 @@ class map_impl : public refcounted map_impl &operator = (const map_impl &) = delete; - virtual linear_combination column (unsigned i) const = 0; + virtual const linear_combination column (unsigned i) const = 0; + virtual const linear_combination column_copy (unsigned i) const { return column (i); } linear_combination map (const linear_combination &lc) const { @@ -657,7 +658,11 @@ class explicit_map_impl : public map_impl { } ~explicit_map_impl () { } - linear_combination column (unsigned i) const { return columns[i]; } + const linear_combination column (unsigned i) const { return columns[i]; } + const linear_combination column_copy (unsigned i) const + { + return linear_combination (COPY, columns[i]); + } }; template @@ -667,7 +672,7 @@ class zero_map_impl : public map_impl zero_map_impl (ptr > fromto) : map_impl(fromto) { } zero_map_impl (ptr > from, ptr > to) : map_impl(from, to) { } - linear_combination column (unsigned i) const { return linear_combination (this->to); } + const linear_combination column (unsigned i) const { return linear_combination (this->to); } }; template @@ -677,7 +682,7 @@ class id_map_impl : public map_impl id_map_impl (ptr > fromto) : map_impl(fromto) { } id_map_impl (ptr > from, ptr > to) : map_impl(from, to) { } - linear_combination column (unsigned i) const + const linear_combination column (unsigned i) const { linear_combination r (this->to); r.muladd (1, i); @@ -700,7 +705,7 @@ class composition_impl : public map_impl assert (g->to == f->from); } - linear_combination column (unsigned i) const + const linear_combination column (unsigned i) const { return f->map (g->column (i)); } @@ -721,7 +726,7 @@ class direct_sum_impl : public map_impl { } - linear_combination column (unsigned i) const + const linear_combination column (unsigned i) const { pair p = f->from->project (g->from, i); @@ -756,7 +761,7 @@ class tensor_impl : public map_impl { } - linear_combination column (unsigned i) const + const linear_combination column (unsigned i) const { pair p = f->from->generator_factors (g->from, i); @@ -884,8 +889,10 @@ class mod_map bool operator != (int x) const { return !operator == (x); } - linear_combination column (unsigned i) const { return impl->column (i); } - linear_combination operator [] (unsigned i) const { return impl->column (i); } + const linear_combination column (unsigned i) const { return impl->column (i); } + const linear_combination operator [] (unsigned i) const { return impl->column (i); } + + const linear_combination column_copy (unsigned i) const { return impl->column_copy (i); } linear_combination map (const linear_combination &lc) const { return impl->map (lc); } mod_map compose (const mod_map &m) const @@ -1637,8 +1644,8 @@ mod_map::operator + (const mod_map &m) const basedvector, 1> v (impl->from->dim ()); for (unsigned i = 1; i <= m.impl->from->dim (); i ++) - v[i] = column (i) + m.columns (i); - return explicit_map_impl (impl->from, impl->to, v); + v[i] = column (i) + m.column (i); + return mod_map (IMPL, new explicit_map_impl (impl->from, impl->to, v)); } template ptr > diff --git a/cube.h b/cube.h index 49bd26e..2583cab 100644 --- a/cube.h +++ b/cube.h @@ -16,8 +16,8 @@ template class cube /* of resolutions */ { public: - typedef typename R::linear_combination linear_combination; - typedef typename R::linear_combination_const_iter linear_combination_const_iter; + typedef ::linear_combination linear_combination; + typedef ::linear_combination_const_iter linear_combination_const_iter; public: bool markedp_only; diff --git a/knotkit.h b/knotkit.h index c0165ae..bfa33db 100644 --- a/knotkit.h +++ b/knotkit.h @@ -14,11 +14,13 @@ class knot_diagram; #include #include +#include #include #include #include #include +#include #include unsigned rolfsen_crossing_knots (unsigned n); diff --git a/lib/pair.h b/lib/pair.h index ce37dbb..46d8f51 100644 --- a/lib/pair.h +++ b/lib/pair.h @@ -54,8 +54,9 @@ public: triple (const F &first_, const S &second_, const T &third_) : first(first_), second(second_), third(third_) { } + triple (reader &r); ~triple () { } - + triple &operator = (const triple &t) { first = t.first; @@ -81,13 +82,11 @@ public: return 1; else if (second > t.second) return 0; - - if (third < t.third) - return 1; - else if (third > t.third) - return 0; + + return third < t.third; } + void write_self (writer &w) const; hash_t hash_self () const { hash_t h = hash (first); @@ -95,3 +94,19 @@ public: return hash_combine (h, hash (third)); } }; + +template +triple::triple (reader &r) +{ + read (r, first); + read (r, second); + read (r, third); +} + +template void +triple::write_self (writer &w) const +{ + write (w, first); + write (w, second); + write (w, third); +} diff --git a/main.cpp b/main.cpp index 9ba848a..1a7fc05 100644 --- a/main.cpp +++ b/main.cpp @@ -127,189 +127,143 @@ rank_lte (multivariate_laurentpoly p, int main () { -#if 0 - ullmanset<1> empty; - assert (empty.size () == 0); - assert (empty.card () == 0); - assert (empty.is_empty ()); + for (int a = 1; a >= 0; a --) + for (unsigned i = 1; i <= 9; i ++) + for (unsigned j = 1; j <= htw_knots (i, a); j ++) + { + knot_diagram kd (htw_knot (i, a, j)); + show (kd); newline (); + + cube c (kd); + mod_map d = c.compute_d (1, 0, 0, 0, 0); - ullmanset<1> empty2; - assert (empty == empty2); + chain_complex_simplifier s (c.khC, d, 1); + + steenrod_square sq (c, d, s); + mod_map sq1 = sq.sq1 (); + // display ("sq1:\n", sq1); - ullmanset<1> emptycopy (COPY, empty); - assert (emptycopy == empty); + mod_map sq2 = sq.sq2 (); + if (a) + assert (sq2 == 0); + else + display ("sq2:\n", sq2); - ullmanset<1> s (50, {1,2,5,5,2,1,3,4}); - assert (s.card () == 5); - - ullmanset<1> scopy (COPY, s); - assert (scopy == s); - - printf ("s:"); - for (int x : s) - printf (" %d", x); - newline (); -#endif - - map m = { { 5, "foo" }, { 11, "bar" } }; - - hashmap m2 = { { 5, "foo" }, { 11, "bar" }, { 37, "baz" } }; - for (std::pair i : m2) - { - printf ("%d -> %s\n", i.first, i.second); - } + assert (sq1.compose (sq1) == 0); + assert (sq2.compose (sq2) + sq1.compose (sq2).compose (sq1) == 0); + } #if 0 - set s {1,2,5,5,2,1,3,4}; - printf ("s:"); - for (int x : s) - printf (" %d", x); - newline (); -#endif + typedef Z2 R; -#if 0 - basedvector v (4); - v[1] = 2; - v[2] = -1; - v[3] = 0xbf32813; - v[4] = -352182; - - vector sleb (512); - for (unsigned i = 0; i < 512; i ++) - sleb[i] = ((int)i << 23); - - printf ("sleb:\n"); - for (unsigned i = 0; i < 512; i ++) - printf (" %d\n", sleb[i]); - - vector uleb (512); - for (unsigned i = 0; i < 512; i ++) - uleb[i] = ((unsigned)i << 23); - - printf ("uleb:\n"); - for (unsigned i = 0; i < 512; i ++) - printf (" %u\n", uleb[i]); - - polynomial one (1); - polynomial x (1, 1); - polynomial bigp (Z (367521)); - - polynomial t = x + bigp*one; - polynomial p = (t*t*t + t*t + t + one)*(bigp*t*t + bigp*bigp*t + bigp*bigp*bigp)*(bigp*bigp*t*t*t + bigp*t*t + t + one); - display ("p: ", p); - - { - file_writer w ("test.dat"); - write (w, v); - write (w, p); - write (w, sleb); - write (w, uleb); - } - - { - gzfile_writer w ("test.2.dat.gz"); - write (w, v); - write (w, p); - write (w, sleb); - write (w, uleb); - } - - system ("gzip -cd test.2.dat.gz > test.2.dat"); - - { - file_reader r ("test.dat"); - basedvector v2 (r); - polynomial p2 (r); - - vector sleb2 (r); - printf ("sleb2:\n"); - for (unsigned i = 0; i < 512; i ++) - printf (" %d\n", sleb2[i]); - - vector uleb2 (r); - assert (v == v2); - assert (p == p2); - assert (sleb == sleb2); - assert (uleb == uleb2); - } - - system ("gzip -c9 test.dat > test.dat.gz"); - - { - gzfile_reader r ("test.dat.gz"); - basedvector v2 (r); - polynomial p2 (r); - vector sleb2 (r); - vector uleb2 (r); - assert (v == v2); - assert (p == p2); - assert (sleb == sleb2); - assert (uleb == uleb2); - } -#endif - -#if 0 - -#if 0 - test_ring (0); - test_ring > (0); - - test_field (); - test_field > (); - test_field (); - // test_field > > (); -#endif - -#if 0 - for (unsigned i = 1; i <= 10; i ++) - for (unsigned j = 1; j <= rolfsen_crossing_knots (i); j ++) - { - knot_diagram kd (rolfsen_knot (i, j)); -#endif - for (unsigned i = 1; i <= 12; i ++) + for (unsigned i = 12; i <= 12; i ++) for (unsigned j = 1; j <= htw_knots (i, 0); j ++) { knot_diagram kd (htw_knot (i, 0, j)); + show (kd); newline (); + cube c (kd); + mod_map d = c.compute_d (1, 0, 0, 0, 0); + + chain_complex_simplifier s (c.khC, d, 1); + // assert (s.new_d == 0); + + printf ("|s.new_C| = %d\n", s.new_C->dim ()); + } +#endif + +#if 1 + map, + set > > kh_knot_map; + + for (int a = 1; a >= 0; a --) + for (unsigned i = 1; i <= 12; i ++) + for (unsigned j = 1; j <= htw_knots (i, a); j ++) + { + knot_diagram kd (htw_knot (i, a, j)); + kd.marked_edge = 1; + show (kd); newline (); + + cube c (kd, 1); + mod_map d = c.compute_d (1, 0, 0, 0, 0); + sseq_builder b (c.khC, d); + sseq ss = b.build_sseq (); + + multivariate_laurentpoly P = ss.pages[1].poincare_polynomial (ss.bounds); + kh_knot_map[P].push (triple (i, a, j)); + } + + { + writer w ("kh_knot_map.dat"); + write (w, kh_knot_map); + } +#endif + +#if 0 + reader r ("kh_knot_map.dat"); + map, + set > > kh_knot_map (r); + + for (map, + set > >::const_iter i = kh_knot_map; i; i ++) + { + printf ("P = "); display (i.key ()); + for (set_const_iter > j = i.val (); j; j ++) + { + knot_diagram kd (htw_knot (j.val ().first, + j.val ().second, + j.val ().third)); + printf (" "); + show (kd); + newline (); + } + } +#endif + +#if 0 + typedef Z2 F; + typedef fraction_field > R; + + for (unsigned i = 1; i <= 10; i ++) + for (unsigned j = 1; j <= htw_knots (i, 0); j ++) + { + knot_diagram kd (htw_knot (i, 0, j)); kd.marked_edge = 1; show (kd); newline (); - spanning_tree_complex c (kd); + spanning_tree_complex st (kd); - mod_map > > E2_d = c.twisted_d2 (); - assert (E2_d.compose (E2_d) == 0); - // display ("E2_d:\n", E2_d); + mod_map d2 = st.twisted_d2 (); + assert (d2.compose (d2) == 0); - chain_complex_simplifier > > E2_s (c.C, E2_d, 2); - assert (E2_s.new_d == 0); + mod_map d2U1 = st.twisted_d2Un (1); + // mod_map d2U1 = st.twisted_d2U1_test (); - multivariate_laurentpoly E3_p = E2_s.new_C->free_delta_poincare_polynomial (); - printf ("E3_p = "); show (E3_p); newline (); - - mod_map > > tt_d = c.totally_twisted_kh_d (); - assert (tt_d.compose (tt_d) == 0); - // display ("tt_d:\n", tt_d); + assert (d2.compose (d2U1) + d2U1.compose (d2) == 0); - chain_complex_simplifier > > tt_s (c.C, tt_d, 2); - assert (tt_s.new_d == 0); + mod_map d2U2 = st.twisted_d2Un (2); + assert (d2.compose (d2U2) + d2U2.compose (d2) + d2U1.compose (d2U1) == 0); + + mod_map d2U3 = st.twisted_d2Un (3); + assert (d2.compose (d2U3) + d2U3.compose (d2) + + d2U2.compose (d2U1) + d2U1.compose (d2U2) == 0); - multivariate_laurentpoly tt_p = tt_s.new_C->free_delta_poincare_polynomial (); - printf ("tt_p = "); show (tt_p); newline (); + mod_map d2U4 = st.twisted_d2Un (4); + assert (d2.compose (d2U4) + d2U4.compose (d2) + + d2U3.compose (d2U1) + d2U1.compose (d2U3) + + d2U2.compose (d2U2) == 0); + + mod_map d2U5 = st.twisted_d2Un (5); + assert (d2.compose (d2U5) + d2U5.compose (d2) + + d2U4.compose (d2U1) + d2U1.compose (d2U4) + + d2U3.compose (d2U2) + d2U2.compose (d2U3) == 0); - cube kh_c (kd, 1); - mod_map kh_d = kh_c.compute_d (1, 0, 0, 0, 0); - - sseq_builder builder (kh_c.khC, kh_d); - sseq ss = builder.build_sseq (); - multivariate_laurentpoly kh_p = ss.pages[1].delta_poincare_polynomial (ss.bounds); - printf ("kh_p = "); show (kh_p); newline (); - - if (tt_p != kh_p) - printf (" > tt_p != kh_p!!\n"); - - if (! rank_lte (E3_p, tt_p)) - printf (" > rank E2 > rank tt!!\n"); + mod_map d2U6 = st.twisted_d2Un (6); + assert (d2.compose (d2U6) + d2U6.compose (d2) + + d2U5.compose (d2U1) + d2U1.compose (d2U5) + + d2U4.compose (d2U2) + d2U2.compose (d2U4) + + d2U3.compose (d2U3) == 0); } #endif } diff --git a/simplify_chain_complex.h b/simplify_chain_complex.h new file mode 100644 index 0000000..ec330e3 --- /dev/null +++ b/simplify_chain_complex.h @@ -0,0 +1,248 @@ + +template class simplified_complex_generators +{ + unsigned new_n; + ptr > C; + basedvector new_C_to_C_generator; + +public: + simplified_complex_generators (const simplified_complex_generators &g) + : new_n(g.new_n), + C(g.C), + new_C_to_C_generator(g.new_C_to_C_generator) + { } + simplified_complex_generators (unsigned new_n_, + ptr > C_, + basedvector new_C_to_C_generator_) + : new_n(new_n_), + C(C_), + new_C_to_C_generator(new_C_to_C_generator_) + { } + ~simplified_complex_generators () { } + + simplified_complex_generators &operator = (const simplified_complex_generators &); // doesn't exist + + unsigned dim () const { return new_n; } + unsigned free_rank () const { return new_n; } + grading generator_grading (unsigned i) const { return C->generator_grading (new_C_to_C_generator[i]); } + void show_generator (unsigned i) const { C->show_generator (new_C_to_C_generator[i]); } + R generator_ann (unsigned i) const { abort (); } +}; + +template +class chain_complex_simplifier +{ + public: + ptr > C; + unsigned n; // |C| + mod_map d; + + ptr > new_C; + mod_map new_d; + + // pi : C -> new_C + mod_map pi; + + // iota : new_C -> C + mod_map iota; + + private: + set canceled; + + basedvector cancel_binv; + basedvector cancel_j; + basedvector, 1> cancel_di; + + basedvector, 1> new_d_columns; + basedvector, 1> preim; + + basedvector, 1> iota_columns; + + void cancel (unsigned i, R b, unsigned j); + + public: + chain_complex_simplifier (ptr > C_, + const mod_map &d_, + int dh); + +}; + +template void +chain_complex_simplifier::cancel (unsigned i, R b, unsigned j) +{ + assert (i != j); + assert (b.is_unit ()); + + R binv = b.recip (); + + canceled.push (i); + canceled.push (j); + + new_d_columns[i].yank (j); + preim[j].yank (i); + + for (linear_combination_const_iter k = new_d_columns[i]; k; k ++) + preim[k.key ()].yank (i); + for (set_const_iter k = preim[i]; k; k ++) + new_d_columns[k.val ()].yank (i); + for (linear_combination_const_iter k = new_d_columns[j]; k; k ++) + preim[k.key ()].yank (j); + + for (set_const_iter kk = preim[j]; kk; kk ++) + { + unsigned k = kk.val (); + R a = new_d_columns[k](j); + assert (a != 0); + + iota_columns[k].mulsub (a * binv, iota_columns[i]); + } + + iota_columns[i].clear (); + iota_columns[j].clear (); + + for (set_const_iter kk = preim[j]; kk; kk ++) + { + unsigned k = kk.val (); + R a = new_d_columns[k](j); + assert (a != 0); + + R abinv = a * binv; + + for (linear_combination_const_iter ll = new_d_columns[i]; ll; ll ++) + { + unsigned ell = ll.key (); + R c = ll.val (); + + assert (! (canceled % k)); + assert (! (canceled % ell)); + assert (k != i); + assert (k != j); + assert (ell != i); + assert (ell != j); + assert (ell != k); + + new_d_columns[k].mulsub (abinv * c, ell); + if (new_d_columns[k] % ell) + preim[ell] += k; + else + preim[ell] -= k; + } + } + + for (set_const_iter k = preim[j]; k; k ++) + new_d_columns[k.val ()].yank (j); + + cancel_binv.append (binv); + cancel_j.append (j); + cancel_di.append (new_d_columns[i]); + + new_d_columns[i] = linear_combination (); + preim[i].clear (); + new_d_columns[j].clear (); + preim[j].clear (); +} + +template +chain_complex_simplifier::chain_complex_simplifier (ptr > C_, + const mod_map &d_, + int dh) + : C(C_), n(C_->dim ()), d(d_), + new_d_columns(n), + preim(n), + iota_columns(n) +{ + for (unsigned i = 1; i <= n; i ++) + { + new_d_columns[i] = d.column_copy (i); + + for (linear_combination_const_iter j = new_d_columns[i]; j; j ++) + preim[j.key ()].push (i); + + linear_combination x (C); + x.muladd (1, i); + iota_columns[i] = x; + } + + for (unsigned i = n; i >= 1; i --) + { + if (canceled % i) + continue; + + grading igr = C->generator_grading (i); + for (linear_combination_const_iter j = new_d_columns[i]; j; j ++) + { + grading jgr = C->generator_grading (j.key ()); + assert (jgr.h >= igr.h); + if (j.val ().is_unit () + && jgr.h - igr.h == dh) + { + cancel (i, j.val (), j.key ()); + break; + } + } + } + + // ??? might not be completely simplified + + unsigned new_n = n - canceled.card (); + basedvector new_C_to_C_generator (new_n), + C_to_new_C_generator (n); + for (unsigned i = 1, j = 1; i <= n; i ++) + { + if (canceled % i) + { + C_to_new_C_generator[i] = 0; + continue; + } + + C_to_new_C_generator[i] = j; + new_C_to_C_generator[j] = i; + j ++; + } + + new_C = (new base_module > + (simplified_complex_generators (new_n, C, new_C_to_C_generator))); + map_builder db (new_C); + + for (unsigned i = 1; i <= new_n; i ++) + { + unsigned i0 = new_C_to_C_generator[i]; + + for (linear_combination_const_iter j0 = new_d_columns[i0]; j0; j0 ++) + { + unsigned j = C_to_new_C_generator[j0.key ()]; + assert (j != 0); + + db[i].muladd (j0.val (), j); + } + } + new_d = mod_map (db); + + map_builder iotab (new_C, C); + + for (unsigned i = 1; i <= new_n; i ++) + iotab[i] = iota_columns[new_C_to_C_generator[i]]; + iota = mod_map (iotab); + + map_builder pib (C, new_C); + for (unsigned i = 1; i <= new_n; i ++) + pib[new_C_to_C_generator[i]].muladd (1, i); + + while (cancel_binv.size () > 0) + { + R binv = cancel_binv.pop (); + unsigned j = cancel_j.pop (); + linear_combination di = cancel_di.pop (); + + for (linear_combination_const_iter ll = di; ll; ll ++) + { + R c = ll.val (); + pib[j].mulsub (binv * c, pib[ll.key ()]); + } + } + pi = mod_map (pib); + + assert (d.compose (iota) == iota.compose (new_d)); + assert (new_d.compose (pi) == pi.compose (d)); + assert (pi.compose (iota) == mod_map (new_C, 1)); +} diff --git a/spanning_tree_complex.h b/spanning_tree_complex.h index 82abe9d..385a9a0 100644 --- a/spanning_tree_complex.h +++ b/spanning_tree_complex.h @@ -25,6 +25,9 @@ class spanning_tree_complex mod_map totally_twisted_kh_d () const; mod_map twisted_d2 () const; + mod_map twisted_d2Un (unsigned n) const; + + mod_map twisted_d2U1_test () const; }; template @@ -210,6 +213,291 @@ spanning_tree_complex::twisted_d2 () const return mod_map (b); } +template mod_map > > +spanning_tree_complex::twisted_d2Un (unsigned n) const +{ + assert (kd.marked_edge); + assert (n >= 1); + + map_builder b (C); + + basedvector edge_weight (kd.num_edges ()); + for (unsigned i = 1; i <= kd.num_edges (); i ++) + edge_weight[i] = 1; // (1 << i); + + int total_weight = 0; + for (unsigned i = 1; i <= kd.num_edges (); i ++) + total_weight += edge_weight[i]; + + int shift = 2 * total_weight * n; + + for (unsigned i = 1; i <= trees.size (); i ++) + { + set t = trees[i]; + + smallbitset r (kd.n_crossings); + for (unsigned k = 1; k <= kd.n_crossings; k ++) + { + if ((edge_height[k] == 1) == (t % k)) + r.push (k); + } + smoothing s (kd, r); + + for (set_const_iter ee = t; ee; ee ++) + { + unsigned e = ee.val (); + + if (edge_height[e] != 0) + continue; + + for (unsigned f = 1; f <= bg.num_edges (); f ++) + { + if (edge_height[f] != 1 || (t % f)) + continue; + + set t2 (COPY, t); + t2.yank (e); + t2.push (f); + unsigned j = tree_idx(t2, 0); + if (j == 0) + continue; + + bool neg = 0; + for (unsigned a = kd.marked_edge;;) + { + unsigned p = s.edge_to (kd, a); + if (kd.ept_crossing[p] == e) + { + if (s.is_crossing_from_ept (kd, r, p)) + neg = s.crossing_from_inside (kd, r, e); + else + neg = s.crossing_to_inside (kd, r, e); + break; + } + else if (kd.ept_crossing[p] == f) + { + if (s.is_crossing_from_ept (kd, r, p)) + neg = s.crossing_from_inside (kd, r, f); + else + neg = s.crossing_to_inside (kd, r, f); + break; + } + + a = s.next_edge (kd, r, a); + assert (a != kd.marked_edge); + } + + set neither (COPY, t); + neither.yank (e); + + smallbitset neither_r (kd.n_crossings); + for (unsigned k = 1; k <= kd.n_crossings; k ++) + { + if ((edge_height[k] == 1) == (neither % k)) + neither_r.push (k); + } + smoothing neither_s (kd, neither_r); + + set both (COPY, t); + both.push (f); + + int A = 0; + for (unsigned k = 1; k <= kd.num_edges (); k ++) + { + if (neither_s.edge_circle[k] + != neither_s.edge_circle[kd.marked_edge]) + A += edge_weight[k]; + } + + smallbitset both_r (kd.n_crossings); + for (unsigned k = 1; k <= kd.n_crossings; k ++) + { + if ((edge_height[k] == 1) == (both % k)) + both_r.push (k); + } + smoothing both_s (kd, both_r); + + int B = 0; + for (unsigned k = 1; k <= kd.num_edges (); k ++) + { + if (both_s.edge_circle[k] + != both_s.edge_circle[kd.marked_edge]) + B += edge_weight[k]; + } + + assert (A >= 0 && B >= 0); + + R x; + + for (int k = 1; k <= (int)n; k ++) + { + if (n % k == 0) + { + int l = n / k; + assert (k * l == (int)n); + + assert (shift >= k*A + l*B); + + if (neg) // ^ (bool)((n + 1) & 1)) + { + x += R (polynomial (1, (unsigned)(shift + k*A + l*B))); + x += R (polynomial (1, (unsigned)(shift - k*A - l*B))); + } + else + { + x += R (polynomial (1, (unsigned)(shift - k*A + l*B))); + x += R (polynomial (1, (unsigned)(shift + k*A - l*B))); + } + } + } + + b[i].muladd (x, j); + } + } + } + + return mod_map (b); +} + +template mod_map > > +spanning_tree_complex::twisted_d2U1_test () const +{ + assert (kd.marked_edge); + + map_builder b (C); + + basedvector edge_weight (kd.num_edges ()); + for (unsigned i = 1; i <= kd.num_edges (); i ++) + edge_weight[i] = 1; // (1 << i); + + int total_weight = 0; + for (unsigned i = 1; i <= kd.num_edges (); i ++) + total_weight += edge_weight[i]; + + int shift = 2 * total_weight; + + for (unsigned i = 1; i <= trees.size (); i ++) + { + set t = trees[i]; + + smallbitset r (kd.n_crossings); + for (unsigned k = 1; k <= kd.n_crossings; k ++) + { + if ((edge_height[k] == 1) == (t % k)) + r.push (k); + } + smoothing s (kd, r); + + for (set_const_iter ee = t; ee; ee ++) + { + unsigned e = ee.val (); + + if (edge_height[e] != 0) + continue; + + for (unsigned f = 1; f <= bg.num_edges (); f ++) + { + if (edge_height[f] != 1 || (t % f)) + continue; + + set t2 (COPY, t); + t2.yank (e); + t2.push (f); + unsigned j = tree_idx(t2, 0); + if (j == 0) + continue; + + bool neg = 0; + for (unsigned a = kd.marked_edge;;) + { + unsigned p = s.edge_to (kd, a); + if (kd.ept_crossing[p] == e) + { + if (s.is_crossing_from_ept (kd, r, p)) + neg = s.crossing_from_inside (kd, r, e); + else + neg = s.crossing_to_inside (kd, r, e); + break; + } + else if (kd.ept_crossing[p] == f) + { + if (s.is_crossing_from_ept (kd, r, p)) + neg = s.crossing_from_inside (kd, r, f); + else + neg = s.crossing_to_inside (kd, r, f); + break; + } + + a = s.next_edge (kd, r, a); + assert (a != kd.marked_edge); + } + + set neither (COPY, t); + neither.yank (e); + + smallbitset neither_r (kd.n_crossings); + for (unsigned k = 1; k <= kd.n_crossings; k ++) + { + if ((edge_height[k] == 1) == (neither % k)) + neither_r.push (k); + } + smoothing neither_s (kd, neither_r); + + set both (COPY, t); + both.push (f); + + int A = 0; + for (unsigned k = 1; k <= kd.num_edges (); k ++) + { + if (neither_s.edge_circle[k] + != neither_s.edge_circle[kd.marked_edge]) + A += edge_weight[k]; + } + + smallbitset both_r (kd.n_crossings); + for (unsigned k = 1; k <= kd.n_crossings; k ++) + { + if ((edge_height[k] == 1) == (both % k)) + both_r.push (k); + } + smoothing both_s (kd, both_r); + + int B = 0; + for (unsigned k = 1; k <= kd.num_edges (); k ++) + { + if (both_s.edge_circle[k] + != both_s.edge_circle[kd.marked_edge]) + B += edge_weight[k]; + } + + assert (A >= 0 && B >= 0); + + printf ("A = %d, B = %d\n", A, B); + + R x; + + // worked: + // bad: -, -/+, + + assert (shift >= A && shift >= B); + if (neg) + { + x += R (polynomial (1, (unsigned)(shift + A))); + x += R (polynomial (1, (unsigned)(shift - B))); + } + else + { + x += R (polynomial (1, (unsigned)(shift - A))); + x += R (polynomial (1, (unsigned)(shift + B))); + } + + b[i].muladd (x, j); + } + } + } + + return mod_map (b); +} + template mod_map > > spanning_tree_complex::totally_twisted_kh_d () const { diff --git a/steenrod_square.cpp b/steenrod_square.cpp new file mode 100644 index 0000000..7376a69 --- /dev/null +++ b/steenrod_square.cpp @@ -0,0 +1,547 @@ + +#include + +steenrod_square::steenrod_square (const cube &cor_, + mod_map &d_, + const chain_complex_simplifier &s_) + : cor(cor_), d(d_), s(s_) +{ + for (unsigned i = 1; i <= cor.khC->dim (); i ++) + { + grading igr = cor.khC->generator_grading (i); + KGij[igr].push (i); + } +} + +Z2 +steenrod_square::sC (unsigned x, unsigned y) const +{ + pair x_sm = cor.generator_state_monomial (x), + y_sm = cor.generator_state_monomial (y); + + unsigned changed = x_sm.first ^ y_sm.first; + assert (unsigned_bitcount (changed) == 1); + unsigned i = unsigned_ffs (changed); + + unsigned k = 0; + for (unsigned j = 1; j < i; j ++) + { + if (unsigned_bittest (x_sm.first, j)) + k ++; + } + + return Z2 (k); +} + +Z2 +steenrod_square::fC (unsigned x, unsigned y) const +{ + pair x_sm = cor.generator_state_monomial (x), + y_sm = cor.generator_state_monomial (y); + + unsigned changed = x_sm.first ^ y_sm.first; + assert (unsigned_bitcount (changed) == 2); + + unsigned i = unsigned_ffs (changed), + j = unsigned_ffs (unsigned_bitclear (changed, i)); + assert (i < j); + + unsigned ki = 0; + for (unsigned l = 1; l < i; l ++) + { + if (unsigned_bittest (x_sm.first, l)) + ki ++; + } + unsigned kj = 0; + for (unsigned l = i + 1; l < j; l ++) + { + if (unsigned_bittest (x_sm.first, l)) + kj ++; + } + + return Z2 (ki) * Z2 (kj); +} + +triple, // G1cx + map, // bx + map > // sx +steenrod_square::boundary_matching (grading cgr, + linear_combination c, + unsigned x) const +{ + set G1cx; + map bx; + map sx; + + for (linear_combination_const_iter yy = c; yy; yy ++) + { + assert (yy.val () == 1); + unsigned y = yy.key (); + + if (d[y](x) == 1) + G1cx.push (y); + } + assert (is_even (G1cx.card ())); + + for (set_const_iter i = G1cx; i; i ++) + { + unsigned y = i.val (); + i ++; + unsigned bxy = i.val (); + + bx.push (y, bxy); + bx.push (bxy, y); + + sx.push (y, 0); + sx.push (bxy, Z2 (1) + sC (x, y) + sC (x, bxy)); + } + +#ifndef NDEBUG + for (set_const_iter yy = G1cx; yy; yy ++) + { + unsigned y = yy.val (); + assert (sx(y) + sx(bx(y)) == Z2 (1) + sC (x, y) + sC (x, bx(y))); + } +#endif + + return triple, // G1cx + map, // bx + map > // sx + (G1cx, bx, sx); +} + +mod_map +steenrod_square::sq1 () const +{ + map_builder b (s.new_C); + + for (unsigned i = 1; i <= s.new_C->dim (); i ++) + { + grading cgr = s.new_C->generator_grading (i); + linear_combination c = s.iota[i]; + assert (d.map (c) == 0); + + grading gr2 (cgr.h + 1, cgr.q); + if (! (KGij % gr2)) + continue; + + linear_combination sq1c (cor.khC); + + for (set_const_iter x = KGij[gr2]; x; x ++) + { + triple, + map, + map > t = boundary_matching (cgr, c, x.val ()); + + Z2 a = 0; + for (set_const_iter y = t.first; y; y ++) + a += t.third(y.val ()); + + sq1c.muladd (a, x.val ()); + } + // display ("sq1c:\n", sq1c); + assert (d.map (sq1c) == 0); + + b[i].muladd (1, s.pi.map (sq1c)); + } + + return mod_map (b); +} + +set > +steenrod_square::make_G2cx (grading cgr, + linear_combination c, + unsigned x) const +{ + set > G2cx; + + for (linear_combination_const_iter yy = c; yy; yy ++) + { + unsigned y = yy.key (); + + for (linear_combination_const_iter zz = d[y]; zz; zz ++) + { + unsigned z = zz.key (); + + if (d[z](x) == 1) + G2cx.push (pair (z, y)); + } + } + return G2cx; +} + +class graph +{ +public: + unsigned n_vertices; + unsigned n_edges; + + basedvector z; + basedvector y; + map, unsigned> zy_vertex; + + unsigned vertex (unsigned z, unsigned y) const + { + return zy_vertex(pair (z, y)); + } + + basedvector edge_from, edge_to; + set edge_oriented; + basedvector edge_label; + + map > incident_edges; + +public: + graph (set > G2cx); + ~graph () { } + + void add_edge (unsigned from_z, unsigned from_y, + unsigned to_z, unsigned to_y, + Z2 label, + bool oriented); + + unsigned num_components () const; + Z2 f () const; // sum of weights + Z2 g () const; +}; + +graph::graph (set > G2cx) + : n_vertices(G2cx.card ()), + n_edges(0), + z(n_vertices), + y(n_vertices) +{ + unsigned j = 1; + for (set_const_iter > i = G2cx; i; i ++, j ++) + { + z[j] = i.val ().first; + y[j] = i.val ().second; + zy_vertex.push (i.val (), j); + } +#ifndef NDEBUG + for (unsigned i = 1; i <= n_vertices; i ++) + { + assert (i == vertex (z[i], y[i])); + } +#endif + assert (j == n_vertices + 1); +} + +void +graph::add_edge (unsigned from_z, unsigned from_y, + unsigned to_z, unsigned to_y, + Z2 label, + bool oriented) +{ + unsigned e = ++ n_edges; + + unsigned from = vertex (from_z, from_y), + to = vertex (to_z, to_y); + + edge_from.append (from); + edge_to.append (to); + edge_label.append (label); + assert (e == edge_label.size ()); + + if (oriented) + edge_oriented.push (e); + + incident_edges[from].push (e); + incident_edges[to].push (e); +} + +unsigned +graph::num_components () const +{ + unionfind<1> u (n_vertices); + for (unsigned i = 1; i <= n_edges; i ++) + u.join (edge_from[i], edge_to[i]); + return u.num_sets (); +} + +Z2 +graph::f () const +{ + Z2 a = 0; + for (unsigned i = 1; i <= n_edges; i ++) + a += edge_label[i]; + return a; +} + +Z2 +graph::g () const +{ + set done; + + set A, + B; + for (unsigned i = 1; i <= n_edges; i ++) + { + if (done % i) + continue; + + unsigned j = i; + unsigned v = edge_to[j]; + for (;;) + { + if (edge_oriented % j) + { + if (edge_to[j] == v) + A.push (j); + else + B.push (j); + } + done.push (j); + + if (incident_edges[v].head () == j) + j = incident_edges[v].tail (); + else + { + assert (j == incident_edges[v].tail ()); + j = incident_edges[v].head (); + } + if (v == edge_to[j]) + v = edge_from[j]; + else + { + assert (v == edge_from[j]); + v = edge_to[j]; + } + + if (j == i) + { + assert (v == edge_to[i]); + break; + } + } + } + + assert (Z2 (A.card ()) == Z2 (B.card ())); + return Z2 (A.card ()); +} + +pair, + map > +steenrod_square::ladybug_matching (unsigned x, unsigned y) const +{ + set Gxy; + map lxy; + + for (linear_combination_const_iter zz = d[y]; zz; zz ++) + { + unsigned z = zz.key (); + + if (d[z](x) == 1) + Gxy.push (z); + } + + if (Gxy.card () == 2) + { + unsigned z1 = Gxy.head (), + z2 = Gxy.tail (); + assert (z1 != z2); + + lxy.push (z1, z2); + lxy.push (z2, z1); + } + else if (Gxy.card () == 4) + { + pair x_sm = cor.generator_state_monomial (x), + y_sm = cor.generator_state_monomial (y); + + unsigned changed = x_sm.first ^ y_sm.first; + assert (unsigned_bitcount (changed) == 2); + unsigned i = unsigned_ffs (changed), + j = unsigned_ffs (unsigned_bitclear (changed, i)); + + unsigned s00 = y_sm.first; + unsigned s01 = unsigned_bitset (s00, i); + unsigned s10 = unsigned_bitset (s00, j); + + smoothing from_s (cor.kd, smallbitset (cor.n_crossings, s00)); + + basedvector from_circle_edge_rep (from_s.n_circles); + for (unsigned j = 1; j <= cor.kd.num_edges (); j ++) + from_circle_edge_rep[from_s.edge_circle[j]] = j; + + unsigned e1 = cor.kd.ept_edge (cor.kd.crossings[i][1]), + e3 = cor.kd.ept_edge (cor.kd.crossings[i][3]); + + unsigned p = from_s.edge_circle[e1]; + assert (p == from_s.edge_circle[e3]); + assert (unsigned_bittest (y_sm.second, p)); // p+ + + smoothing conf01 (cor.kd, smallbitset (cor.n_crossings, s01)), + conf10 (cor.kd, smallbitset (cor.n_crossings, s10)); + + assert (conf01.edge_circle[e1] != conf01.edge_circle[e3]); + assert (conf10.edge_circle[e1] != conf10.edge_circle[e3]); + + unsigned m01 = 0, + m10 = 0; + for (unsigned i = 1; i <= from_s.n_circles; i ++) + { + if (unsigned_bittest (y_sm.second, i)) + { + m01 = unsigned_bitset (m01, conf01.edge_circle[from_circle_edge_rep[i]]); + m10 = unsigned_bitset (m10, conf10.edge_circle[from_circle_edge_rep[i]]); + } + } + + unsigned m01_a = unsigned_bitclear (unsigned_bitset (m01, + conf01.edge_circle[e1]), + conf01.edge_circle[e3]), + m10_a = unsigned_bitclear (unsigned_bitset (m10, + conf10.edge_circle[e1]), + conf10.edge_circle[e3]); + unsigned z01_a = cor.generator (s01, m01_a), + z10_a = cor.generator (s10, m10_a); + assert (Gxy(z01_a) && Gxy(z10_a)); + lxy.push (z01_a, z10_a); + lxy.push (z10_a, z01_a); + + unsigned m01_b = unsigned_bitclear (unsigned_bitset (m01, + conf01.edge_circle[e3]), + conf01.edge_circle[e1]), + m10_b = unsigned_bitclear (unsigned_bitset (m10, + conf10.edge_circle[e3]), + conf10.edge_circle[e1]); + unsigned z01_b = cor.generator (s01, m01_b), + z10_b = cor.generator (s10, m10_b); + assert (Gxy(z01_b) && Gxy(z10_b)); + lxy.push (z01_b, z10_b); + lxy.push (z10_b, z01_b); + } + else + assert (Gxy.card () == 0); + + assert (Gxy.card () == lxy.card ()); + + return pair, map > (Gxy, lxy); +} + +Z2 +steenrod_square::sq2_coeff (grading cgr, + linear_combination c, + unsigned x) const +{ + set > G2cx = make_G2cx (cgr, c, x); + + graph G (G2cx); + + set ys; + for (unsigned i = 1; i <= G.n_vertices; i ++) + ys += G.y[i]; + + for (set_const_iter yy = ys; yy; yy ++) + { + unsigned y = yy.val (); + + pair, + map > p = ladybug_matching (x, y); + + set Gxy = p.first; + map lxy = p.second; + + for (set_const_iter zz = Gxy; zz; zz ++) + { + unsigned z = zz.val (); + unsigned lxyz = lxy(z); + if (z < lxyz) + { + G.add_edge (z, y, + lxyz, y, + fC (x, y), 0); + } + } + } + + set zs; + for (unsigned i = 1; i <= G.n_vertices; i ++) + zs += G.z[i]; + + for (set_const_iter zz = zs; zz; zz ++) + { + unsigned z = zz.val (); + triple, + map, + map > t = boundary_matching (cgr, c, z); + + set Gcz = t.first; + map bz = t.second; + map sz = t.third; + + for (set_const_iter yy = Gcz; yy; yy ++) + { + unsigned y = yy.val (); + + unsigned bzy = bz(y); + if (y < bzy) + { + if (sz(y) == 0 + && sz(bzy) == 1) + { + G.add_edge (z, y, + z, bzy, + 0, 1); + } + else if (sz(y) == 1 + && sz(bzy) == 0) + { + G.add_edge (z, bzy, + z, y, + 0, 1); + } + else + { + assert (sz(y) == sz(bzy)); + G.add_edge (z, y, + z, bzy, + 0, 0); + } + } + } + } + +#ifndef NDEBUG + for (unsigned i = 1; i <= G.n_edges; i ++) + { + assert (G.incident_edges[i].card () == 2); + } +#endif + + // printf ("G.n_edges = %d\n", G.n_edges); + // printf ("#|G| = %d\n", G.num_components ()); + + return (Z2 (G.num_components ()) + + G.f () + + G.g ()); +} + +mod_map +steenrod_square::sq2 () const +{ + map_builder b (s.new_C); + + for (unsigned i = 1; i <= s.new_C->dim (); i ++) + { + grading cgr = s.new_C->generator_grading (i); + linear_combination c = s.iota[i]; + assert (d.map (c) == 0); + + grading gr2 (cgr.h + 2, cgr.q); + if (! (KGij % gr2)) + continue; + + linear_combination sq2c (cor.khC); + for (set_const_iter x = KGij[gr2]; x; x ++) + sq2c.muladd (sq2_coeff (cgr, c, x.val ()), x.val ()); + + // display ("sq2c:\n", sq2c); + assert (d.map (sq2c) == 0); + + b[i].muladd (1, s.pi.map (sq2c)); + } + + return mod_map (b); +} diff --git a/steenrod_square.h b/steenrod_square.h new file mode 100644 index 0000000..176c5ce --- /dev/null +++ b/steenrod_square.h @@ -0,0 +1,42 @@ + +class steenrod_square +{ + // cube of resolutions (in steenrod_square, c will denote a cycle) + const cube &cor; + + mod_map d; + const chain_complex_simplifier &s; + + map > KGij; + + // sC, fC take generators as input, not hypercube vertices + Z2 sC (unsigned x, unsigned y) const; + Z2 fC (unsigned x, unsigned y) const; + + pair, // Gxy + map // lxy + > ladybug_matching (unsigned x, unsigned y) const; + + triple, // Gcx + map, // bx + map // sx + > boundary_matching (grading cgr, + linear_combination c, + unsigned x) const; + + set > make_G2cx (grading cgr, + linear_combination c, + unsigned x) const; + Z2 sq2_coeff (grading cgr, + linear_combination c, + unsigned x) const; + + public: + steenrod_square (const cube &cor_, + mod_map &d_, + const chain_complex_simplifier &s_); + ~steenrod_square () { } + + mod_map sq1 () const; + mod_map sq2 () const; +};