548 lines
12 KiB
C++
548 lines
12 KiB
C++
|
|
#include <knotkit.h>
|
|
|
|
steenrod_square::steenrod_square (const cube<Z2> &cor_,
|
|
mod_map<Z2> &d_,
|
|
const chain_complex_simplifier<Z2> &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<unsigned, unsigned> 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<unsigned, unsigned> 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<set<unsigned>, // G1cx
|
|
map<unsigned, unsigned>, // bx
|
|
map<unsigned, Z2> > // sx
|
|
steenrod_square::boundary_matching (grading cgr,
|
|
linear_combination<Z2> c,
|
|
unsigned x) const
|
|
{
|
|
set<unsigned> G1cx;
|
|
map<unsigned, unsigned> bx;
|
|
map<unsigned, Z2> sx;
|
|
|
|
for (linear_combination_const_iter<Z2> 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<unsigned> 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<unsigned> 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<set<unsigned>, // G1cx
|
|
map<unsigned, unsigned>, // bx
|
|
map<unsigned, Z2> > // sx
|
|
(G1cx, bx, sx);
|
|
}
|
|
|
|
mod_map<Z2>
|
|
steenrod_square::sq1 () const
|
|
{
|
|
map_builder<Z2> b (s.new_C);
|
|
|
|
for (unsigned i = 1; i <= s.new_C->dim (); i ++)
|
|
{
|
|
grading cgr = s.new_C->generator_grading (i);
|
|
linear_combination<Z2> c = s.iota[i];
|
|
assert (d.map (c) == 0);
|
|
|
|
grading gr2 (cgr.h + 1, cgr.q);
|
|
if (! (KGij % gr2))
|
|
continue;
|
|
|
|
linear_combination<Z2> sq1c (cor.khC);
|
|
|
|
for (set_const_iter<unsigned> x = KGij[gr2]; x; x ++)
|
|
{
|
|
triple<set<unsigned>,
|
|
map<unsigned, unsigned>,
|
|
map<unsigned, Z2> > t = boundary_matching (cgr, c, x.val ());
|
|
|
|
Z2 a = 0;
|
|
for (set_const_iter<unsigned> 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<Z2> (b);
|
|
}
|
|
|
|
set<pair<unsigned, unsigned> >
|
|
steenrod_square::make_G2cx (grading cgr,
|
|
linear_combination<Z2> c,
|
|
unsigned x) const
|
|
{
|
|
set<pair<unsigned, unsigned> > G2cx;
|
|
|
|
for (linear_combination_const_iter<Z2> yy = c; yy; yy ++)
|
|
{
|
|
unsigned y = yy.key ();
|
|
|
|
for (linear_combination_const_iter<Z2> zz = d[y]; zz; zz ++)
|
|
{
|
|
unsigned z = zz.key ();
|
|
|
|
if (d[z](x) == 1)
|
|
G2cx.push (pair<unsigned, unsigned> (z, y));
|
|
}
|
|
}
|
|
return G2cx;
|
|
}
|
|
|
|
class graph
|
|
{
|
|
public:
|
|
unsigned n_vertices;
|
|
unsigned n_edges;
|
|
|
|
basedvector<unsigned, 1> z;
|
|
basedvector<unsigned, 1> y;
|
|
map<pair<unsigned, unsigned>, unsigned> zy_vertex;
|
|
|
|
unsigned vertex (unsigned z, unsigned y) const
|
|
{
|
|
return zy_vertex(pair<unsigned, unsigned> (z, y));
|
|
}
|
|
|
|
basedvector<unsigned, 1> edge_from, edge_to;
|
|
set<unsigned> edge_oriented;
|
|
basedvector<Z2, 1> edge_label;
|
|
|
|
map<unsigned, set<unsigned> > incident_edges;
|
|
|
|
public:
|
|
graph (set<pair<unsigned, unsigned> > 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<pair<unsigned, unsigned> > G2cx)
|
|
: n_vertices(G2cx.card ()),
|
|
n_edges(0),
|
|
z(n_vertices),
|
|
y(n_vertices)
|
|
{
|
|
unsigned j = 1;
|
|
for (set_const_iter<pair<unsigned, unsigned> > 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<unsigned> done;
|
|
|
|
set<unsigned> 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<set<unsigned>,
|
|
map<unsigned, unsigned> >
|
|
steenrod_square::ladybug_matching (unsigned x, unsigned y) const
|
|
{
|
|
set<unsigned> Gxy;
|
|
map<unsigned, unsigned> lxy;
|
|
|
|
for (linear_combination_const_iter<Z2> 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<unsigned, unsigned> 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<unsigned, 1> 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<set<unsigned>, map<unsigned, unsigned> > (Gxy, lxy);
|
|
}
|
|
|
|
Z2
|
|
steenrod_square::sq2_coeff (grading cgr,
|
|
linear_combination<Z2> c,
|
|
unsigned x) const
|
|
{
|
|
set<pair<unsigned, unsigned> > G2cx = make_G2cx (cgr, c, x);
|
|
|
|
graph G (G2cx);
|
|
|
|
set<unsigned> ys;
|
|
for (unsigned i = 1; i <= G.n_vertices; i ++)
|
|
ys += G.y[i];
|
|
|
|
for (set_const_iter<unsigned> yy = ys; yy; yy ++)
|
|
{
|
|
unsigned y = yy.val ();
|
|
|
|
pair<set<unsigned>,
|
|
map<unsigned, unsigned> > p = ladybug_matching (x, y);
|
|
|
|
set<unsigned> Gxy = p.first;
|
|
map<unsigned, unsigned> lxy = p.second;
|
|
|
|
for (set_const_iter<unsigned> 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<unsigned> zs;
|
|
for (unsigned i = 1; i <= G.n_vertices; i ++)
|
|
zs += G.z[i];
|
|
|
|
for (set_const_iter<unsigned> zz = zs; zz; zz ++)
|
|
{
|
|
unsigned z = zz.val ();
|
|
triple<set<unsigned>,
|
|
map<unsigned, unsigned>,
|
|
map<unsigned, Z2> > t = boundary_matching (cgr, c, z);
|
|
|
|
set<unsigned> Gcz = t.first;
|
|
map<unsigned, unsigned> bz = t.second;
|
|
map<unsigned, Z2> sz = t.third;
|
|
|
|
for (set_const_iter<unsigned> 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<Z2>
|
|
steenrod_square::sq2 () const
|
|
{
|
|
map_builder<Z2> b (s.new_C);
|
|
|
|
for (unsigned i = 1; i <= s.new_C->dim (); i ++)
|
|
{
|
|
grading cgr = s.new_C->generator_grading (i);
|
|
linear_combination<Z2> c = s.iota[i];
|
|
assert (d.map (c) == 0);
|
|
|
|
grading gr2 (cgr.h + 2, cgr.q);
|
|
if (! (KGij % gr2))
|
|
continue;
|
|
|
|
linear_combination<Z2> sq2c (cor.khC);
|
|
for (set_const_iter<unsigned> 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<Z2> (b);
|
|
}
|