#ifndef _KNOTKIT_ALGEBRA_MODULE_H #define _KNOTKIT_ALGEBRA_MODULE_H template class mod_map; template class mod_span; template class free_submodule; template class quotient_module; template class direct_sum; template class tensor_product; template class hom_module; /* `module' is a bigraded module over a ring R. */ template class module : public refcounted { private: friend class reader; friend class writer; unsigned id; static unsigned id_counter; static map > > reader_id_module; static map, ptr > > direct_sum_idx; static map, ptr > > tensor_product_idx; static map, ptr > > hom_module_idx; public: module () { id_counter ++; id = id_counter; } module (const module &) = delete; virtual ~module () { } module &operator = (const module &) = delete; public: // the number of generators; n virtual unsigned dim () const = 0; // r; 1 <= r <= n virtual unsigned free_rank () const = 0; virtual grading generator_grading (unsigned i) const = 0; virtual void show_generator (unsigned i) const = 0; // r < i <= n virtual R generator_ann (unsigned i) const = 0; basedvector grading_vector () const; set gradings () const; bool is_free () const { return dim () == free_rank (); } bool is_zero (R c, unsigned i) const { if (i <= free_rank ()) return c == 0; else { // ??? abort (); // return generator_ann (i) | c; } } R annihilator (R c, unsigned i) const { R iann = generator_ann (i); return iann.div (iann.gcd (c)); } bool isomorphic (ptr > m) const; ptr > quotient (const mod_span &span) const; ptr > quotient (ptr > m) const; ptr > submodule (const mod_span &span) const; multivariate_laurentpoly free_poincare_polynomial () const; template multivariate_laurentpoly> free_poincare_polynomial () const; multivariate_laurentpoly free_delta_poincare_polynomial () const; multivariate_laurentpoly free_ell_poincare_polynomial () const; ptr > add (basedvector >, 1> compound_summands) const; ptr > add (ptr > m) const { basedvector >, 1> summands (2); summands[1] = this; summands[2] = m; return add (summands); } // g -> (g, 0) unsigned inject_1 (unsigned g, ptr > m) const { return g; } // g -> (0, g) unsigned inject_2 (ptr > m, unsigned g) const { return dim () + g; } pair project (ptr > m, unsigned g) const { if (g <= dim ()) return pair (1, g); else return pair (2, g - dim ()); } ptr > tensor (ptr > m) const { basedvector >, 1> factors (2); factors[1] = this; factors[2] = m; return tensor (factors); } ptr > hom (ptr > to) const; pair generator_factors (ptr > m, unsigned g) const { pair p ((g - 1) % dim () + 1, (g - 1) / dim () + 1); assert (g == tensor_generators (p.first, m, p.second)); return p; } static ptr > tensor (basedvector >, 1> compound_factors); unsigned tensor_generators (unsigned i, ptr > m, unsigned j) const { return (i - 1) + (j - 1) * dim () + 1; } virtual void append_direct_summands (basedvector >, 1> &summands) const { summands.append (this); } virtual void append_tensor_factors (basedvector >, 1> &factors) const { factors.append (this); } ptr > graded_piece (grading hq) const; void write_self (writer &w) const { w.write_mod (this); } void show_self () const; void display_self () const; }; template unsigned module::id_counter = 0; template map > > module::reader_id_module; template map, ptr > > module::direct_sum_idx; template map, ptr > > module::tensor_product_idx; template map, ptr > > module::hom_module_idx; template class direct_sum : public module { unsigned n; basedvector >, 1> summands; public: direct_sum (basedvector >, 1> summands_) : n(0), summands(summands_) { #ifndef NDEBUG for (unsigned i = 1; i <= summands.size (); i ++) assert (summands[i]->is_free ()); #endif for (unsigned i = 1; i <= summands.size (); i ++) n += summands[i]->dim (); } ~direct_sum () { } unsigned dim () const { return n; } unsigned free_rank () const { return n; } grading generator_grading (unsigned i) const; void show_generator (unsigned i) const; R generator_ann (unsigned i) const { return R (0); } void append_direct_summands (basedvector >, 1> &psummands) const { for (unsigned i = 1; i <= summands.size (); i ++) psummands.append (summands[i]); } unsigned inject (unsigned i, unsigned g) const; pair project (unsigned g) const; }; template grading direct_sum::generator_grading (unsigned i) const { pair p = project (i); return summands[p.first]->generator_grading (p.second); } template void direct_sum::show_generator (unsigned i) const { pair p = project (i); printf ("%d:", p.first); return summands[p.first]->show_generator (p.second); } template unsigned direct_sum::inject (unsigned i, unsigned g) const { assert (i >= 1 && i <= summands.size ()); for (unsigned j = 1; j < i; j ++) g += summands[j]->dim (); return g; } template pair direct_sum::project (unsigned g) const { assert (g <= n); unsigned g0 = g; for (unsigned j = 1; j <= summands.size (); j ++) { if (g <= summands[j]->dim ()) { assert (inject (j, g) == g0); return pair (j, g); } else g -= summands[j]->dim (); } abort (); // shouldn't get here } template ptr > module::add (basedvector >, 1> compound_summands) const { basedvector >, 1> summands; for (unsigned i = 1; i <= compound_summands.size (); i ++) compound_summands[i]->append_direct_summands (summands); basedvector summand_ids (summands.size ()); for (unsigned i = 1; i <= summands.size (); i ++) summand_ids[i] = summands[i]->id; pair > &, bool> p = direct_sum_idx.find (summand_ids); if (!p.second) p.first = new direct_sum (summands); return p.first; } template class tensor_product : public module { unsigned n; basedvector >, 1> factors; basedvector generator_factors (unsigned g) const; public: tensor_product (basedvector >, 1> factors_) : n(1), factors(factors_) { #ifndef NDEBUG for (unsigned i = 1; i <= factors.size (); i ++) assert (factors[i]->is_free ()); #endif for (unsigned i = 1; i <= factors.size (); i ++) n *= factors[i]->dim (); } ~tensor_product () { } unsigned dim () const { return n; } unsigned free_rank () const { return n; } grading generator_grading (unsigned i) const; void show_generator (unsigned i) const; R generator_ann (unsigned i) const { return R (0); } unsigned tensor_generators (basedvector gs) const; void append_tensor_factors (basedvector >, 1> &pfactors) const { for (unsigned i = 1; i <= factors.size (); i ++) pfactors.append (factors[i]); } }; template ptr > module::tensor (basedvector >, 1> compound_factors) { basedvector >, 1> factors; for (unsigned i = 1; i <= compound_factors.size (); i ++) compound_factors[i]->append_tensor_factors (factors); basedvector factor_ids (factors.size ()); for (unsigned i = 1; i <= factors.size (); i ++) factor_ids[i] = factors[i]->id; pair > &, bool> p = tensor_product_idx.find (factor_ids); if (!p.second) p.first = new tensor_product (factors); return p.first; } template grading tensor_product::generator_grading (unsigned i) const { basedvector gs = generator_factors (i); assert (gs.size () == factors.size ()); grading gr; for (unsigned i = 1; i <= factors.size (); i ++) gr += factors[i]->generator_grading (gs[i]); return gr; } template void tensor_product::show_generator (unsigned i) const { basedvector gs = generator_factors (i); assert (gs.size () == factors.size ()); printf ("o("); for (unsigned i = 1; i <= factors.size (); i ++) { if (i > 1) printf (","); factors[i]->show_generator (gs[i]); } printf (")"); } template unsigned tensor_product::tensor_generators (basedvector gs) const { assert (gs.size () == factors.size ()); unsigned r = gs[gs.size ()] - 1; for (unsigned i = gs.size () - 1; i >= 1; i --) { r *= factors[i]->dim (); r += gs[i] - 1; } r ++; return r; } template basedvector tensor_product::generator_factors (unsigned g) const { basedvector r (factors.size ()); unsigned g0 = g; g --; for (unsigned i = 1; i <= factors.size (); i ++) { r[i] = (g % factors[i]->dim ()) + 1; g /= factors[i]->dim (); } assert (g == 0); assert (tensor_generators (r) == g0); return r; } template class hom_module : public module { public: unsigned n; ptr > from; ptr > to; public: hom_module (ptr > from_, ptr > to_) : from(from_), to(to_) { assert (from->is_free () && to->is_free ()); n = from->dim () * to->dim (); } ~hom_module () { } // e_ij -> ij pair generator_indices (unsigned g) const { unsigned d = from->dim (); unsigned g0 = g; g --; pair p ((g % d) + 1, (g / d) + 1); assert (generator (p.first, p.second) == g0); return p; } // ij -> e_ij unsigned generator (unsigned i, unsigned j) const { return (i - 1) + (j - 1) * from->dim () + 1; } unsigned dim () const { return n; } unsigned free_rank () const { return n; } grading generator_grading (unsigned i) const; void show_generator (unsigned i) const; R generator_ann (unsigned i) const { return R (0); } linear_combination map_as_element (const mod_map &m) const; }; template ptr > module::hom (ptr > to) const { pair > &, bool> p = hom_module_idx.find (pair (id, to->id)); if (!p.second) p.first = new hom_module (this, to); return p.first; } template grading hom_module::generator_grading (unsigned i) const { pair p = generator_indices (i); return (to->generator_grading (p.second) - from->generator_grading (p.first)); } template void hom_module::show_generator (unsigned i) const { pair p = generator_indices (i); printf ("("); from->show_generator (p.first); printf (" -> "); to->show_generator (p.second); printf (")"); } template class base_module : public module { private: G g; public: base_module () = delete; base_module (const G &g_) : g(g_) { } ~base_module () { } base_module &operator = (const base_module &) = delete; unsigned dim () const { return g.dim (); } unsigned free_rank () const { return g.free_rank (); } grading generator_grading (unsigned i) const { return g.generator_grading (i); } void show_generator (unsigned i) const { g.show_generator (i); } R generator_ann (unsigned i) const { return g.generator_ann (i); } }; template class explicit_module : public module { unsigned r; basedvector ann; basedvector hq; public: explicit_module () = delete; explicit_module (unsigned r_, basedvector ann_, basedvector hq_) : r(r_), ann(ann_), hq(hq_) { assert (hq.size () == r + ann.size ()); } explicit explicit_module (unsigned r_, basedvector hq_) : r(r_), hq(hq_) { } ~explicit_module () { } explicit_module &operator = (const explicit_module &) = delete; unsigned dim () const { return r + ann.size (); } unsigned free_rank () const { return r; } grading generator_grading (unsigned i) const { return hq[i]; } void show_generator (unsigned i) const { printf ("%d", i); } R generator_ann (unsigned i) const { return ann[i - r]; } }; template class free_submodule : public module { friend class module; ptr > parent; basedvector, 1> gens; basedvector pivots; public: free_submodule (ptr > parent_, basedvector, 1> gens_, basedvector pivots_) : parent(parent_), gens(gens_), pivots(pivots_) { } ~free_submodule () { } free_submodule &operator = (const free_submodule &) = delete; ptr > parent_module () const { return parent; } unsigned dim () const { return gens.size (); } unsigned free_rank () const { return gens.size (); } grading generator_grading (unsigned i) const { return gens[i].hq (); } void show_generator (unsigned i) const { show (gens[i]); } R generator_ann (unsigned i) const { abort (); } linear_combination inject_generator (unsigned i) const { return gens[i]; } linear_combination inject (linear_combination v) const; mod_map injection_map () const; linear_combination restrict (linear_combination v0) const; ptr > restrict_submodule (ptr > m) const; ptr > intersection (ptr > m) const; ptr > plus (ptr > m) const; }; template class quotient_module : public module { friend class module; ptr > parent; // note: these get filled in by module::quotient basedvector ann; // parent lc representing i basedvector, 1> rep; // map from parent generator to lc in quotient basedvector, 1> pi; public: quotient_module (const quotient_module &) = delete; quotient_module (ptr > parent_) : parent(parent_) { } ~quotient_module () { } quotient_module &operator = (const quotient_module &) = delete; ptr > parent_module () const { return parent; } unsigned dim () const { return rep.size (); } unsigned free_rank () const { return rep.size () - ann.size (); } grading generator_grading (unsigned i) const { return rep[i].hq (); } void show_generator (unsigned i) const { show (rep[i]); printf ("/~"); } R generator_ann (unsigned i) const { unsigned r = free_rank (); assert (i > r); return ann[i - r]; } linear_combination project_generator (unsigned i) const { assert (i >= 1 && i <= parent->dim ()); linear_combination r (this); for (typename map::const_iter j = pi[i]; j; j ++) r.muladd (j.val (), j.key ()); return r; } linear_combination project (linear_combination v) const { assert (v.m == parent); linear_combination r (this); for (linear_combination_const_iter i = v; i; i ++) r.muladd (i.val (), project_generator (i.key ())); return r; } linear_combination generator_rep (unsigned i) const { return rep[i]; } }; template class map_impl : public refcounted { public: ptr > from; ptr > to; public: map_impl (const map_impl &) = delete; map_impl (ptr > fromto) : from(fromto), to(fromto) { } map_impl (ptr > from_, ptr > to_) : from(from_), to(to_) { } virtual ~map_impl () { } map_impl &operator = (const map_impl &) = delete; 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 { linear_combination r (this->to); for (linear_combination_const_iter i = lc; i; i ++) r.muladd (i.val (), column (i.key ())); return r; }; }; template class explicit_map_impl : public map_impl { basedvector, 1> columns; public: explicit_map_impl (ptr > fromto, basedvector, 1> columns_) : map_impl(fromto), columns(columns_) { } explicit_map_impl (ptr > from, ptr > to, basedvector, 1> columns_) : map_impl(from, to), columns(columns_) { } ~explicit_map_impl () { } 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 class zero_map_impl : public map_impl { public: zero_map_impl (ptr > fromto) : map_impl(fromto) { } zero_map_impl (ptr > from, ptr > to) : map_impl(from, to) { } const linear_combination column (unsigned i) const { return linear_combination (this->to); } }; template class id_map_impl : public map_impl { public: id_map_impl (ptr > fromto) : map_impl(fromto) { } id_map_impl (ptr > from, ptr > to) : map_impl(from, to) { } const linear_combination column (unsigned i) const { linear_combination r (this->to); r.muladd (1, i); return r; } }; template class composition_impl : public map_impl { // f(g(x)) ptr > f, g; public: composition_impl (ptr > f_, ptr > g_) : map_impl(g_->from, f_->to), f(f_), g(g_) { assert (g->to == f->from); } const linear_combination column (unsigned i) const { return f->map (g->column (i)); } }; template class direct_sum_impl : public map_impl { // f\oplus g ptr > f, g; public: direct_sum_impl (ptr > f_, ptr > g_) : map_impl(f_->from->add (g_->from), f_->to->add (g_->to)), f(f_), g(g_) { } const linear_combination column (unsigned i) const { pair p = f->from->project (g->from, i); linear_combination r (this->to); if (p.first == 1) { for (linear_combination_const_iter j = f->column (p.second); j; j ++) r.muladd (j.val (), f->to->inject_1 (j.key (), g->to)); } else { assert (p.first == 2); for (linear_combination_const_iter j = g->column (p.second); j; j ++) r.muladd (j.val (), f->to->inject_2 (g->to, j.key ())); } return r; } }; template class tensor_impl : public map_impl { // f\otimes g ptr > f, g; public: tensor_impl (ptr > f_, ptr > g_) : map_impl(f_->from->tensor (g_->from), f_->to->tensor (g_->to)), f(f_), g(g_) { } const linear_combination column (unsigned i) const { pair p = f->from->generator_factors (g->from, i); // ?? return f->column (p.first).tensor (g->column (p.second)); } }; template class map_builder { public: ptr > from, to; basedvector, 1> columns; void init (); public: map_builder (ptr > fromto) : from(fromto), to(fromto) { init (); } map_builder (ptr > fromto, int i) : from(fromto), to(fromto) { init (); if (i == 1) { for (unsigned i = 1; i <= from->dim (); i ++) columns[i].muladd (1, i); } else assert (i == 0); } map_builder (ptr > from_, ptr > to_) : from(from_), to(to_) { init (); } linear_combination &operator [] (unsigned i) { return columns[i]; } const linear_combination &operator [] (unsigned i) const { return columns[i]; } }; template void map_builder::init () { columns.resize (from->dim ()); for (unsigned i = 1; i <= from->dim (); i ++) columns[i] = linear_combination (to); } template class mod_map { // ??? enum impl_ctor { IMPL }; ptr > impl; mod_map (impl_ctor, ptr > impl_) : impl(impl_) { } public: mod_map () { } mod_map (const mod_map &m) : impl(m.impl) { } mod_map (ptr > fromto) : impl(new zero_map_impl(fromto)) { } mod_map (ptr > fromto, int i) { if (i == 1) impl = new id_map_impl (fromto); else { assert (i == 0); impl = new zero_map_impl (fromto); } } mod_map (ptr > from, ptr > to) : impl(new zero_map_impl (from, to)) { } mod_map (ptr > fromto, basedvector, 1> columns) : impl(new explicit_map_impl (fromto, columns)) { } mod_map (ptr > from, ptr > to, basedvector, 1> columns) : impl(new explicit_map_impl (from, to, columns)) { } mod_map (const map_builder &b) : impl(new explicit_map_impl (b.from, b.to, b.columns)) { } mod_map (reader &r) { ptr > from = r.read_mod (); ptr > to = r.read_mod (); basedvector, 1> columns (r); impl = new explicit_map_impl (from, to, columns); } ~mod_map () { } mod_map &operator = (const mod_map &m) { impl = m.impl; return *this; } ptr > domain () const { return impl->from; } ptr > codomain () const { return impl->to; } bool operator == (const mod_map &m) const { assert (impl->from == m.impl->from); assert (impl->to == m.impl->to); for (unsigned i = 1; i <= impl->from->dim (); i ++) { if (impl->column (i) != m.impl->column (i)) return 0; } return 1; } bool operator != (const mod_map &m) const { return !operator == (m); } bool operator == (int x) const { R c (x); assert (c == 0); for (unsigned i = 1; i <= impl->from->dim (); i ++) { if (impl->column (i) != 0) return 0; } return 1; } bool operator != (int x) const { return !operator == (x); } 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 { return mod_map (IMPL, new composition_impl (impl, m.impl)); } // ??? in the sense of direct sum mod_map add (const mod_map &m) const { return mod_map (IMPL, new direct_sum_impl (impl, m.impl)); } mod_map tensor (const mod_map &m) const { return mod_map (IMPL, new tensor_impl (impl, m.impl)); } // ?? add and other map operations should not be explicit mod_map operator + (const mod_map &m) const; mod_map operator * (const R &c) const; bool homogeneous () const; void check_grading (grading delta) const; // inj : ker -> from ptr > kernel () const; // inj : im -> to ptr > image () const; ptr > image (basedvector, 1> vs) const; ptr > cokernel () const; ptr > homology () const; mod_map restrict_from (ptr > new_from) const; mod_map restrict_to (ptr > new_to) const; mod_map restrict (ptr > new_from, ptr > new_to) const; mod_map restrict (ptr > new_fromto) const { return restrict (new_fromto, new_fromto); } mod_map induced_map_to (ptr > new_to); mod_map induced_map (ptr > new_fromto); mod_map graded_piece (grading hq) const; // ??? basedvector, 1> explicit_columns () const; void write_self (writer &w) const { // write explicitly write (w, *impl->from); write (w, *impl->to); write (w, explicit_columns ()); } void show_self () const; void display_self () const; }; template linear_combination hom_module::map_as_element (const mod_map &m) const { assert (from == m.domain () && to == m.codomain ()); linear_combination r (this); for (unsigned i = 1; i <= from->dim (); i ++) { for (linear_combination_const_iter j = m.column (i); j; j ++) r.muladd (j.val (), generator (i, j.key ())); } return r; } template class mod_span { public: basedvector, 1> gens; basedvector pivots; public: mod_span (ptr > mod, basedvector, 1> xs); ~mod_span () { } }; template class quotient_helper { public: ptr > mod; // rows of the presentation matrix basedvector, 1> rows; basedvector, 1> generators; basedvector, 1> generators_inv; bool improve_pivot_row (unsigned i, unsigned j, unsigned i2); bool improve_pivot_column (unsigned i, unsigned j, unsigned j2); void improve_pivot (unsigned i, unsigned j); public: quotient_helper (ptr > mod_, basedvector, 1> rows); void normalize (); }; template bool module::isomorphic (ptr > m) const { if (dim () != m->dim () || free_rank () != m->free_rank ()) return 0; set gradings; for (unsigned i = 1; i <= dim (); i ++) gradings += generator_grading (i); for (set_const_iter i = gradings; i; i ++) { grading hq = i.val (); unsigned hq_r = 0, m_hq_r = 0; basedvector hq_ann, m_hq_ann; for (unsigned i = 1; i <= dim (); i ++) { if (generator_grading (i) == hq) { if (i <= free_rank ()) hq_r ++; else { hq_ann.append (generator_ann (i)); } } if (m->generator_grading (i) == hq) { if (i <= free_rank ()) m_hq_r ++; else { m_hq_ann.append (m->generator_ann (i)); } } } if (hq_r != m_hq_r) return 0; if (hq_ann.size () != m_hq_ann.size ()) return 0; for (unsigned i = 1; i <= hq_ann.size (); i ++) { R a = hq_ann[i], m_a = m_hq_ann[i]; if (! (a | m_a && m_a | a)) return 0; } } return 1; } template quotient_helper::quotient_helper (ptr > mod_, basedvector, 1> rows_) : mod(mod_), rows(rows_), generators(mod->dim ()), generators_inv(mod->dim ()) { assert (mod->dim () == mod->free_rank ()); for (unsigned i = 1; i <= mod->dim (); i ++) { linear_combination v (mod); v.muladd (1, i); generators[i] = v; linear_combination vinv (mod); vinv.muladd (1, i); generators_inv[i] = vinv; } } template bool quotient_helper::improve_pivot_row (unsigned i, unsigned j, unsigned i2) { assert (i != i2); const linear_combination &r = rows[i], &r2 = rows[i2]; R rc = r(j), r2c = r2(j); if (r2c == 0) return 0; #if 0 if (rc | r2c) { R q = r2c.div (rc); rows[i2].mulsub (q, r); assert (rows[i2](j) == 0); return 0; } #endif tuple t = rc.extended_gcd (r2c); assert (get<0> (t) == rc*get<1> (t) + get<2> (t)*r2c); rows[i] = r*get<1> (t) + r2*get<2> (t); rows[i2] = (rc.div (get<0> (t)))*r2 - (r2c.div (get<0> (t)))*r; assert ((rc | r2c) == rc.divides (get<0> (t))); assert (!rc.divides (get<0> (t)) || rows[i2](j) == 0); return !rc.divides (get<0> (t)); } template bool quotient_helper::improve_pivot_column (unsigned i, unsigned j, unsigned j2) { assert (j != j2); #if 0 basedvector, 1> orig_row_image (rows.size ()); for (unsigned k = 1; k <= rows.size (); k ++) { linear_combination r (mod); for (linear_combination_const_iter l = rows[k]; l; l ++) r.muladd (l.val (), generators[l.key ()]); orig_row_image[k] = r; } #endif const linear_combination &r = rows[i]; R rc = r(j), rc2 = r(j2); assert (rc != 0); if (rc2 == 0) return 0; assert (generators[j].hq () == generators[j2].hq ()); #if 0 if (rc | rc2) { R q = rc2.div (rc); for (unsigned k = 1; k <= rows.size (); k ++) { linear_combination &rk = rows[k]; rk.mulsub (rk(j) * q, j2); } assert (r(j2) == 0); return 0; } #endif tuple t = rc.extended_gcd (rc2); assert (get<0> (t) == rc*get<1> (t) + get<2> (t)*rc2); for (unsigned k = 1; k <= rows.size (); k ++) { linear_combination &rk = rows[k]; R rkc = rk(j), rkc2 = rk(j2); rk.set_coeff (rkc*get<1> (t) + rkc2*get<2> (t), j); rk.set_coeff (rkc2*(rc.div (get<0> (t))) - rkc*(rc2.div (get<0> (t))), j2); } linear_combination g = generators[j], g2 = generators[j2]; assert (g.hq () == g2.hq ()); generators[j] = (rc.div (get<0> (t))) * g + (rc2.div (get<0> (t))) * g2; generators[j2] = get<1> (t) * g2 - get<2> (t) * g; #if 0 for (unsigned k = 1; k <= rows.size (); k ++) { linear_combination r2 (mod); for (linear_combination_const_iter l = rows[k]; l; l ++) r2.muladd (l.val (), generators[l.key ()]); assert (r2 == orig_row_image[k]); } #endif for (unsigned k = 1; k <= mod->dim (); k ++) { linear_combination &ginv = generators_inv[k]; R d = ginv(j), d2 = ginv(j2); ginv.set_coeff (get<1> (t)*d + get<2> (t)*d2, j); ginv.set_coeff (rc.div (get<0> (t)) * d2 - rc2.div (get<0> (t)) * d, j2); } #if 0 for (unsigned k = 1; k <= mod->dim (); k ++) { linear_combination r (mod); r.muladd (1, k); linear_combination r2 (mod); for (linear_combination_const_iter l = generators_inv[k]; l; l ++) r2.muladd (l.val (), generators[l.key ()]); assert (r == r2); } #endif assert ((rc | rc2) == rc.divides (get<0> (t))); assert (!rc.divides (get<0> (t)) || r(j2) == 0); return !rc.divides (get<0> (t)); } template void quotient_helper::improve_pivot (unsigned i, unsigned j) { for (;;) { bool changed = 0; for (unsigned k = 1; k <= rows.size (); k ++) { if (k == i) continue; if (improve_pivot_row (i, j, k)) changed = 1; } for (unsigned k = 1; k <= mod->dim (); k ++) { if (k == j) continue; if (improve_pivot_column (i, j, k)) changed = 1; } #if 0 L: for (linear_combination_const_iter k = rows[i]; k; k ++) { if (k.key () != j) { if (improve_pivot_column (i, j, k.key ())) changed = 1; goto L; } } #endif if (!changed) return; } } template void quotient_helper::normalize () { for (unsigned i = 1; i <= rows.size (); i ++) { if (rows[i] == 0) continue; pair p = rows[i].head (); improve_pivot (i, p.first); } for (unsigned i = 1; i <= rows.size (); i ++) { if (rows[i] == 0) continue; assert (rows[i].card () == 1); pair p = rows[i].head (); grading ihq = generators[p.first].hq (); for (unsigned j = i; j <= rows.size (); j ++) { if (rows[j] == 0) continue; assert (rows[j].card () == 1); pair q = rows[j].head (); grading jhq = generators[q.first].hq (); if (ihq != jhq) continue; if (p.second | q.second) continue; rows[i] += rows[j]; improve_pivot (i, p.first); #ifndef NDEBUG assert (rows[i].card () == 1); pair p2 = rows[i].head (); assert (p2.first == p.first); assert (rows[j].card () == 1); pair q2 = rows[j].head (); assert (q2.first == q.first); assert (p2.second | q2.second); #endif } } } template ptr > module::quotient (const mod_span &span) const { unsigned n = dim (); quotient_helper h (this, span.gens); h.normalize (); basedvector ann (n); for (unsigned i = 1; i <= h.rows.size (); i ++) { if (h.rows[i] == 0) continue; assert (h.rows[i].card () == 1); pair p = h.rows[i].head (); ann[p.first] = p.second; } ptr > Q = new quotient_module (this); unsigned quot_r = 0; basedvector, 1> quot_rep; basedvector generator_quot_gen (n); for (unsigned i = 1; i <= n; i ++) generator_quot_gen[i] = 0; for (unsigned i = 1; i <= n; i ++) { if (ann[i] == 0) { quot_r ++; quot_rep.append (h.generators[i]); generator_quot_gen[i] = quot_r; } } unsigned quot_n = quot_r; basedvector quot_ann; for (unsigned i = 1; i <= n; i ++) { R a = ann[i]; if (a != 0 && !a.is_unit ()) { quot_n ++; quot_rep.append (h.generators[i]); assert (generator_quot_gen[i] == 0); generator_quot_gen[i] = quot_n; quot_ann.append (a); } } basedvector, 1> quot_pi (n); for (unsigned i = 1; i <= n; i ++) { map v; for (linear_combination_const_iter j = h.generators_inv[i]; j; j ++) { unsigned qg = generator_quot_gen[j.key ()]; if (qg) v.push (qg, j.val ()); } quot_pi[i] = v; } assert (quot_n >= quot_r); assert (quot_rep.size () == quot_n); assert (quot_ann.size () == quot_n - quot_r); Q->rep = quot_rep; Q->pi = quot_pi; Q->ann = quot_ann; assert (Q->dim () == quot_n); assert (Q->free_rank () == quot_r); return Q; } template ptr > module::quotient (ptr > m) const { assert (m->parent_module () == this); // ??? duplicate mod_span span (this, m->gens); return quotient (span); } template ptr > module::submodule (const mod_span &span) const { assert (free_rank () == dim ()); return new free_submodule (this, span.gens, span.pivots); } template multivariate_laurentpoly module::free_poincare_polynomial () const { multivariate_laurentpoly r; for (unsigned i = 1; i <= free_rank (); i ++) { grading hq = generator_grading (i); multivariate_laurent_monomial m; m.push_exponent (1, hq.h); m.push_exponent (2, hq.q); r.muladdeq (1, m); } return r; } template template multivariate_laurentpoly> module::free_poincare_polynomial() const { multivariate_laurentpoly> r; for (unsigned i = 1; i <= free_rank (); i ++) { grading hq = generator_grading (i); multivariate_laurent_monomial m; m.push_exponent (1, hq.h); m.push_exponent (2, hq.q); r.muladdeq (1, m); } return r; } template multivariate_laurentpoly module::free_delta_poincare_polynomial () const { multivariate_laurentpoly r; for (unsigned i = 1; i <= free_rank (); i ++) { grading hq = generator_grading (i); multivariate_laurent_monomial m; m.push_exponent (1, hq.q - 2*hq.h); r.muladdeq (1, m); } return r; } template multivariate_laurentpoly module::free_ell_poincare_polynomial () const { multivariate_laurentpoly r; for (unsigned i = 1; i <= free_rank (); i ++) { grading hq = generator_grading (i); multivariate_laurent_monomial m; m.push_exponent (1, hq.h - hq.q); r.muladdeq (1, m); } return r; } template basedvector module::grading_vector () const { basedvector v (dim ()); for (unsigned i = 1; i <= dim (); i ++) v[i] = generator_grading (i); return v; } template set module::gradings () const { set gs; for (unsigned i = 1; i <= dim (); i ++) gs += generator_grading (i); return gs; } template ptr > module::graded_piece (grading hq) const { basedvector, 1> s; for (unsigned i = 1; i <= dim (); i ++) { grading ihq = generator_grading (i); if (ihq.h == hq.h && ihq.q == hq.q) { linear_combination v (this); v.muladd (1, i); s.append (v); } } mod_span span (this, s); return submodule (span); } template void module::show_self () const { printf ("module/"); R::show_ring (); printf (" %p %d", this, dim ()); } template void module::display_self () const { show_self (); newline (); printf (" free_rank %d\n", free_rank ()); for (unsigned i = 1; i <= dim (); i ++) { printf (" %d ", i); show (generator_grading (i)); printf (" "); show_generator (i); if (i > free_rank ()) { printf (": "); R iann = generator_ann (i); if (iann.is_unit ()) printf ("0\n"); else { R::show_ring (); if (iann != 0) { printf ("/"); show (iann); R::show_ring (); } } } newline (); } } template linear_combination free_submodule::inject (linear_combination v) const { linear_combination r (parent); for (linear_combination_const_iter i = v; i; i ++) r.muladd (i.val (), gens[i.key ()]); return r; } template mod_map free_submodule::injection_map () const { return mod_map (this, parent, gens); } template mod_map mod_map::induced_map_to (ptr > new_to) { assert (new_to->parent_module () == impl->to); basedvector, 1> v (impl->from->dim ()); for (unsigned i = 1; i <= impl->from->dim (); i ++) v[i] = new_to->project (column (i)); return new explicit_map_impl (impl->from, new_to, v); } template mod_map mod_map::induced_map (ptr > new_fromto) { assert (impl->from == new_fromto->parent_module ()); assert (impl->to == new_fromto->parent_module ()); // ??? doesn't check induced map is well-defined basedvector, 1> v (new_fromto->dim ()); for (unsigned i = 1; i <= new_fromto->dim (); i ++) v[i] = new_fromto->project (map (new_fromto->generator_rep (i))); return new explicit_map_impl (new_fromto, v); } template mod_map mod_map::graded_piece (grading hq) const { basedvector, 1> v (impl->from->dim ()); for (unsigned i = 1; i <= impl->from->dim (); i ++) { grading ihq = impl->from->generator_grading (i); linear_combination c = column (i); linear_combination d (impl->to); for (linear_combination_const_iter j = c; j; j ++) { grading jhq = impl->from->generator_grading (j.key ()); if (jhq.h - ihq.h == hq.h && jhq.q - ihq.q == hq.q) d.muladd (j.val (), j.key ()); } v[i] = d; } return mod_map (IMPL, new explicit_map_impl (impl->from, impl->to, v)); } template mod_map mod_map::restrict_from (ptr > new_from) const { assert (new_from->parent_module () == impl->from); basedvector, 1> v (new_from->dim ()); for (unsigned i = 1; i <= new_from->dim (); i ++) v[i] = map (new_from->inject_generator (i)); return mod_map (IMPL, new explicit_map_impl (new_from, impl->to, v)); } template mod_map mod_map::restrict_to (ptr > new_to) const { assert (new_to->parent_module () == impl->to); basedvector, 1> v (impl->from->dim ()); for (unsigned i = 1; i <= impl->from->dim (); i ++) v[i] = new_to->restrict (column (i)); return new explicit_map_impl (impl->from, new_to, v); } template mod_map mod_map::restrict (ptr > new_from, ptr > new_to) const { assert (new_from->parent_module () == impl->from); assert (new_to->parent_module () == impl->to); basedvector, 1> v (new_from->dim ()); for (unsigned i = 1; i <= new_from->dim (); i ++) v[i] = new_to->restrict (map (new_from->inject_generator (i))); return mod_map (IMPL, new explicit_map_impl (new_from, new_to, v)); } template linear_combination free_submodule::restrict (linear_combination v0) const { assert (v0.m == parent); linear_combination v (COPY, v0); linear_combination r (this); for (unsigned i = 1; i <= gens.size (); i ++) { unsigned j = pivots[i]; R vc = v(j); if (vc != 0) { const linear_combination &g = gens[i]; R gc = g(j); assert (gc | vc); R q = vc.div (gc); v.mulsub (q, g); r.muladd (q, i); } } assert (v == 0); assert (inject (r) == v0); return r; } template ptr > free_submodule::restrict_submodule (ptr > m) const { assert (m->parent == parent); basedvector, 1> span (m->dim ()); for (unsigned i = 1; i <= m->dim (); i ++) span[i] = restrict (m->inject_generator (i)); mod_span span2 (this, span); return this->submodule (span2); } template ptr > free_submodule::intersection (ptr > m) const { assert (parent == m->parent); unsigned md = m->dim (), d = dim (); basedvector, 1> intr; basedvector, 1> hperp, hproj; basedvector hpivots; for (unsigned i = 1; i <= md; i ++) { linear_combination perp (COPY, m->gens[i]), proj (parent); for (unsigned j = 1; j <= d; j ++) { unsigned k = pivots[j]; if (perp % k) { const linear_combination &g = gens[j]; R c = g(k); R d = perp(k); assert (c | d); R q = d.div (c); perp.mulsub (q, g); proj.mulsub (q, g); assert (! (perp % k)); } } for (unsigned j = 1; j <= hpivots.size (); j ++) { unsigned k = hpivots[j]; if (perp % k) { const linear_combination &h = hperp[j]; R c = h(k); R d = perp(k); assert (c | d); R q = d.div (c); perp.mulsub (q, h); proj.mulsub (q, hproj[j]); assert (! (perp % k)); } } if (perp == 0) intr.append (proj); else { hperp.append (perp); hproj.append (proj); hpivots.append (perp.head ().first); } } mod_span span (parent, intr); return parent->submodule (span); } template ptr > free_submodule::plus (ptr > m) const { assert (parent == m->parent); basedvector, 1> s; for (unsigned i = 1; i <= dim (); i ++) s.append (gens[i]); for (unsigned i = 1; i <= m->dim (); i ++) s.append (m->gens[i]); mod_span span (parent, s); return parent->submodule (span); } template bool mod_map::homogeneous () const { for (unsigned i = 1; i <= impl->from->dim (); i ++) { if (column (i) != 0) { grading dhq = column (i).hq () - impl->from->generator_grading (i); for (unsigned j = i + 1; j <= impl->from->dim (); j ++) { if (column (j) != 0 && dhq != column (j).hq () - impl->from->generator_grading (j)) return 0; } return 1; } } return 1; } template void mod_map::check_grading (grading delta) const { for (unsigned i = 1; i <= impl->from->dim (); i ++) { if (column (i) != 0) assert (column (i).hq () - impl->from->generator_grading (i) == delta); } } template mod_map mod_map::operator * (const R &c) const { basedvector, 1> v (impl->from->dim ()); for (unsigned i = 1; i <= impl->from->dim (); i ++) v[i] = c*column (i); return mod_map (IMPL, new explicit_map_impl (impl->from, impl->to, v)); } template mod_map mod_map::operator + (const mod_map &m) const { assert (impl->from == m.impl->from && impl->to == m.impl->to); basedvector, 1> v (impl->from->dim ()); for (unsigned i = 1; i <= m.impl->from->dim (); i ++) v[i] = column (i) + m.column (i); return mod_map (IMPL, new explicit_map_impl (impl->from, impl->to, v)); } template ptr > mod_map::kernel () const { ptr > from = impl->from, to = impl->to; basedvector, 1> from_xs (from->dim ()); for (unsigned i = 1; i <= from->dim (); i ++) { linear_combination x (from); x.muladd (1, i); from_xs[i] = x; } basedvector, 1> to_xs (COPY2, explicit_columns ()); for (unsigned i = 1; i <= to->dim (); i ++) { linear_combination from_v (from), to_v (to); for (unsigned j = 1; j <= to_xs.size (); j ++) { R to_vc = to_v(i); if (to_vc.is_unit ()) break; linear_combination &to_x = to_xs[j], &from_x = from_xs[j]; R to_xc = to_x(i); if (to_xc == 0) continue; if (to_vc == 0 && to_xc != 0) { to_v += to_x; from_v += from_x; } else if (! (to_vc | to_xc)) { tuple t = to_vc.extended_gcd (to_xc); assert (get<0> (t) == to_vc*get<1> (t) + get<2> (t)*to_xc); to_v = get<1> (t)*to_v + get<2> (t)*to_x; from_v = get<1> (t)*from_v + get<2> (t)*from_x; assert (to_v(i) != 0); } } R to_vc = to_v(i); if (to_vc != 0) { for (unsigned j = 1; j <= to_xs.size (); j ++) { linear_combination &to_x = to_xs[j], &from_x = from_xs[j]; R to_xc = to_x(i); if (to_xc != 0) { assert (to_vc | to_xc); R q = to_xc.div (to_vc); to_x.mulsub (q, to_v); from_x.mulsub (q, from_v); } assert (to_x(i) == 0); } } } mod_span span (from, from_xs); return from->submodule (span); } template ptr > mod_map::image () const { mod_span span (impl->to, explicit_columns ()); return impl->to->submodule (span); } template ptr > mod_map::image (basedvector, 1> vs) const { mod_span span (impl->from, vs); ptr > s = impl->from->submodule (span); mod_map r = restrict_from (s); return r.image (); } template ptr > mod_map::cokernel () const { mod_span span (impl->to, explicit_columns ()); return impl->to->quotient (span); } template ptr > mod_map::homology () const { ptr > ker = kernel (), im = image (); // display ("ker:", *ker); // display ("im:", *im); ptr > im2 = ker->restrict_submodule (im); // display ("im2:", *im2); return ker->quotient (im2); } template basedvector, 1> mod_map::explicit_columns () const { basedvector, 1> v (impl->from->dim ()); for (unsigned i = 1; i <= impl->from->dim (); i ++) v[i] = column (i); return v; } template mod_span::mod_span (ptr > mod, basedvector, 1> xs0) { assert (mod->free_rank () == mod->dim ()); basedvector, 1> xs (COPY2, xs0); for (unsigned i = 1; i <= mod->dim (); i ++) { linear_combination v (mod); for (unsigned j = 1; j <= xs.size (); j ++) { R vc = v(i); if (vc.is_unit ()) break; linear_combination &x = xs[j]; R xc = x(i); if (xc == 0) continue; if (vc == 0) { v += x; } else if (! (vc | xc)) { tuple t = vc.extended_gcd (xc); assert (get<0> (t) == vc*get<1> (t) + get<2> (t)*xc); v = get<1> (t)*v + get<2> (t)*x; assert (v(i) != 0); } } R vc = v(i); if (vc != 0) { for (unsigned j = 1; j <= xs.size (); j ++) { linear_combination &x = xs[j]; R xc = x(i); if (xc != 0) { assert (vc | xc); R q = xc.div (vc); x.mulsub (q, v); } assert (x(i) == 0); } pivots.append (i); gens.append (v); } } } template void mod_map::show_self () const { printf ("mod_map "); show (*impl->from); printf (" -> "); show (*impl->to); } template void mod_map::display_self () const { show_self (); newline (); for (unsigned i = 1; i <= impl->from->dim (); i ++) { printf (" %d ", i); impl->from->show_generator (i); printf (" "); show (impl->from->generator_grading (i)); printf (": "); show (column (i)); newline (); } } // ??? io template void writer::write_mod (ptr > m) { pair p = aw->id_io_id.find (m->id); if (p.second) { write_int ((int)p.first); } else { ++ aw->io_id_counter; unsigned io_id = aw->io_id_counter; p.first = io_id; write_int (- (int)io_id); unsigned n = m->dim (), r = m->free_rank (); write_unsigned (n); write_unsigned (r); for (unsigned i = 1; i <= n; i ++) write (*this, m->generator_grading (i)); for (unsigned i = r + 1; i <= n; i ++) write (*this, m->generator_ann (i)); } } template ptr > reader::read_mod () { int io_id = read_int (); if (io_id < 0) { unsigned n = read_unsigned (); unsigned r = read_unsigned (); basedvector gr (n); for (unsigned i = 1; i <= n; i ++) gr[i] = grading (*this); basedvector ann (n - r); for (unsigned i = r + 1; i <= n; i ++) ann[i - r] = R (*this); ptr > m = new explicit_module (r, ann, gr); ar->io_id_id.push ((unsigned)(-io_id), m->id); module::reader_id_module.push (m->id, m); return m; } else { unsigned id = ar->io_id_id(io_id); return module::reader_id_module(id); } } #endif // _KNOTKIT_ALGEBRA_MODULE_H