forked from glen/dyna3

The extension should also let us work over finite fields of prime order, although we don't need to do that.
204 lines
No EOL
5.8 KiB
Julia
204 lines
No EOL
5.8 KiB
Julia
module Engine
|
|
|
|
export Construction, mprod
|
|
|
|
import Subscripts
|
|
using LinearAlgebra
|
|
using AbstractAlgebra
|
|
using Groebner
|
|
|
|
# --- commutative algebra ---
|
|
|
|
# as of version 0.36.6, AbstractAlgebra only supports ideals in multivariate
|
|
# polynomial rings when coefficients are integers. in `reduce_gens`, the
|
|
# `lmnode` constructor requires < to be defined on the coefficients, and the
|
|
# `reducer_size` heuristic requires `ndigits` to be defined on the coefficients.
|
|
# this patch for `reducer_size` removes the `ndigits` dependency
|
|
##function Generic.reducer_size(f::T) where {U <: MPolyRingElem{<:FieldElement}, V, N, T <: Generic.lmnode{U, V, N}}
|
|
## if f.size != 0.0
|
|
## return f.size
|
|
## end
|
|
## return 0.0 + sum(j^2 for j in 1:length(f.poly))
|
|
##end
|
|
|
|
# as of version 0.36.6, AbstractAlgebra only supports ideals in multivariate
|
|
# polynomial rings when the coefficients are integers. we use Groebner to extend
|
|
# support to rationals and to finite fields of prime order
|
|
Generic.reduce_gens(I::Generic.Ideal{U}) where {T <: FieldElement, U <: MPolyRingElem{T}} =
|
|
Generic.Ideal{U}(base_ring(I), groebner(gens(I)))
|
|
|
|
# --- primitve elements ---
|
|
|
|
abstract type Element{T} end
|
|
|
|
mutable struct Point{T} <: Element{T}
|
|
coords::Vector{MPolyRingElem{T}}
|
|
vec::Union{Vector{MPolyRingElem{T}}, Nothing}
|
|
rel::Nothing
|
|
|
|
## [to do] constructor argument never needed?
|
|
Point{T}(
|
|
coords::Vector{MPolyRingElem{T}} = MPolyRingElem{T}[],
|
|
vec::Union{Vector{MPolyRingElem{T}}, Nothing} = nothing
|
|
) where T = new(coords, vec, nothing)
|
|
end
|
|
|
|
function buildvec!(pt::Point)
|
|
coordring = parent(pt.coords[1])
|
|
pt.vec = [one(coordring), dot(pt.coords, pt.coords), pt.coords...]
|
|
end
|
|
|
|
mutable struct Sphere{T} <: Element{T}
|
|
coords::Vector{MPolyRingElem{T}}
|
|
vec::Union{Vector{MPolyRingElem{T}}, Nothing}
|
|
rel::Union{MPolyRingElem{T}, Nothing}
|
|
|
|
## [to do] constructor argument never needed?
|
|
Sphere{T}(
|
|
coords::Vector{MPolyRingElem{T}} = MPolyRingElem{T}[],
|
|
vec::Union{Vector{MPolyRingElem{T}}, Nothing} = nothing,
|
|
rel::Union{MPolyRingElem{T}, Nothing} = nothing
|
|
) where T = new(coords, vec, rel)
|
|
end
|
|
|
|
function buildvec!(sph::Sphere)
|
|
coordring = parent(sph.coords[1])
|
|
sph.vec = sph.coords
|
|
sph.rel = mprod(sph.coords, sph.coords) + one(coordring)
|
|
end
|
|
|
|
const coordnames = IdDict{Symbol, Vector{Union{Symbol, Nothing}}}(
|
|
nameof(Point) => [nothing, nothing, :xₚ, :yₚ, :zₚ],
|
|
nameof(Sphere) => [:rₛ, :sₛ, :xₛ, :yₛ, :zₛ]
|
|
)
|
|
|
|
coordname(elem::Element, index) = coordnames[nameof(typeof(elem))][index]
|
|
|
|
function pushcoordname!(coordnamelist, indexed_elem::Tuple{Any, Element}, coordindex)
|
|
elemindex, elem = indexed_elem
|
|
name = coordname(elem, coordindex)
|
|
if !isnothing(name)
|
|
subscript = Subscripts.sub(string(elemindex))
|
|
push!(coordnamelist, Symbol(name, subscript))
|
|
end
|
|
end
|
|
|
|
function takecoord!(coordlist, indexed_elem::Tuple{Any, Element}, coordindex)
|
|
elem = indexed_elem[2]
|
|
if !isnothing(coordname(elem, coordindex))
|
|
push!(elem.coords, popfirst!(coordlist))
|
|
end
|
|
end
|
|
|
|
# --- primitive relations ---
|
|
|
|
abstract type Relation{T} end
|
|
|
|
mprod(v, w) = (v[1]*w[2] + w[1]*v[2]) / 2 - dot(v[3:end], w[3:end])
|
|
|
|
# elements: point, sphere
|
|
struct LiesOn{T} <: Relation{T}
|
|
elements::Vector{Element{T}}
|
|
|
|
LiesOn{T}(pt::Point{T}, sph::Sphere{T}) where T = new{T}([pt, sph])
|
|
end
|
|
|
|
equation(rel::LiesOn) = mprod(rel.elements[1].vec, rel.elements[2].vec)
|
|
|
|
# elements: sphere, sphere
|
|
struct AlignsWithBy{T} <: Relation{T}
|
|
elements::Vector{Element{T}}
|
|
cos_angle::T
|
|
|
|
AlignsWithBy{T}(sph1::Sphere{T}, sph2::Sphere{T}, cos_angle::T) where T = new{T}([sph1, sph2], cos_angle)
|
|
end
|
|
|
|
equation(rel::AlignsWithBy) = mprod(rel.elements[1].vec, rel.elements[2].vec) - rel.cos_angle
|
|
|
|
# --- constructions ---
|
|
|
|
mutable struct Construction{T}
|
|
elements::Set{Element{T}}
|
|
relations::Set{Relation{T}}
|
|
|
|
function Construction{T}(; elements = Set{Element{T}}(), relations = Set{Relation{T}}()) where T
|
|
allelements = union(elements, (rel.elements for rel in relations)...)
|
|
new{T}(allelements, relations)
|
|
end
|
|
end
|
|
|
|
function Base.push!(ctx::Construction{T}, elem::Element{T}) where T
|
|
push!(ctx.elements, elem)
|
|
end
|
|
|
|
function Base.push!(ctx::Construction{T}, rel::Relation{T}) where T
|
|
push!(ctx.relations, rel)
|
|
union!(ctx.elements, rel.elements)
|
|
end
|
|
|
|
function realize(ctx::Construction{T}) where T
|
|
# collect coordinate names
|
|
coordnamelist = Symbol[]
|
|
elemenum = enumerate(ctx.elements)
|
|
for coordindex in 1:5
|
|
for indexed_elem in elemenum
|
|
pushcoordname!(coordnamelist, indexed_elem, coordindex)
|
|
end
|
|
end
|
|
|
|
# construct coordinate ring
|
|
coordring, coordqueue = polynomial_ring(parent_type(T)(), coordnamelist, ordering = :degrevlex)
|
|
|
|
# retrieve coordinates
|
|
for (_, elem) in elemenum
|
|
empty!(elem.coords)
|
|
end
|
|
for coordindex in 1:5
|
|
for indexed_elem in elemenum
|
|
takecoord!(coordqueue, indexed_elem, coordindex)
|
|
end
|
|
end
|
|
|
|
# construct coordinate vectors
|
|
for (_, elem) in elemenum
|
|
buildvec!(elem)
|
|
end
|
|
|
|
# turn relations into equations
|
|
eqns = vcat(
|
|
equation.(ctx.relations),
|
|
[elem.rel for elem in ctx.elements if !isnothing(elem.rel)]
|
|
)
|
|
Generic.Ideal(coordring, eqns)
|
|
end
|
|
|
|
end
|
|
|
|
# ~~~ sandbox setup ~~~
|
|
|
|
CoeffType = Rational{Int64}
|
|
|
|
a = Engine.Point{CoeffType}()
|
|
s = Engine.Sphere{CoeffType}()
|
|
a_on_s = Engine.LiesOn{CoeffType}(a, s)
|
|
ctx = Engine.Construction{CoeffType}(elements = Set([a]), relations= Set([a_on_s]))
|
|
ideal_a_s = Engine.realize(ctx)
|
|
|
|
b = Engine.Point{CoeffType}()
|
|
b_on_s = Engine.LiesOn{CoeffType}(b, s)
|
|
Engine.push!(ctx, b)
|
|
Engine.push!(ctx, s)
|
|
Engine.push!(ctx, b_on_s)
|
|
ideal_ab_s = Engine.realize(ctx)
|
|
|
|
spheres = [Engine.Sphere{CoeffType}() for _ in 1:3]
|
|
tangencies = [
|
|
Engine.AlignsWithBy{CoeffType}(
|
|
spheres[n],
|
|
spheres[mod1(n+1, length(spheres))],
|
|
CoeffType(-1//1)
|
|
)
|
|
for n in 1:3
|
|
]
|
|
ctx_chain = Engine.Construction{CoeffType}(elements = Set(spheres), relations = Set(tangencies))
|
|
ideal_chain = Engine.realize(ctx_chain) |