Compare commits
No commits in common. "9d69a900e28e0694b984ed326d3c9b6d6edca668" and "b185fd4b83ff494e36967008ae2f207a190eb610" have entirely different histories.
9d69a900e2
...
b185fd4b83
@ -4,8 +4,6 @@ using Blink
|
||||
using Colors
|
||||
using Printf
|
||||
|
||||
using Main.Engine
|
||||
|
||||
export ConstructionViewer, display!, opentools!, closetools!
|
||||
|
||||
# === Blink utilities ===
|
||||
@ -135,7 +133,7 @@ mprod(v, w) =
|
||||
function display!(viewer::ConstructionViewer, elements::Matrix)
|
||||
# load elements
|
||||
elements_full = []
|
||||
for elt in eachcol(Engine.unmix * elements)
|
||||
for elt in eachcol(elements)
|
||||
if mprod(elt, elt) < 0.5
|
||||
elt_full = [0; elt; fill(0, 26)]
|
||||
else
|
||||
@ -207,16 +205,14 @@ end
|
||||
|
||||
# ~~~ sandbox setup ~~~
|
||||
|
||||
elements = let
|
||||
a = sqrt(BigFloat(3)/2)
|
||||
sqrt(0.5) * BigFloat[
|
||||
1 1 -1 -1 0
|
||||
1 -1 1 -1 0
|
||||
1 -1 -1 1 0
|
||||
0.5 0.5 0.5 0.5 1+a
|
||||
0.5 0.5 0.5 0.5 1-a
|
||||
# in the default view, e4 + e5 is the point at infinity
|
||||
elements = sqrt(0.5) * BigFloat[
|
||||
1 1 -1 -1 0;
|
||||
1 -1 1 -1 0;
|
||||
1 -1 -1 1 0;
|
||||
0 0 0 0 -sqrt(6);
|
||||
1 1 1 1 2
|
||||
]
|
||||
end
|
||||
|
||||
# show construction
|
||||
viewer = Viewer.ConstructionViewer()
|
||||
|
@ -29,7 +29,7 @@ function rand_on_shell(rng::AbstractRNG, shell::T) where T <: Number
|
||||
space_part = rand_on_sphere(rng, T, 4)
|
||||
rapidity = randn(rng, T)
|
||||
sig = sign(shell)
|
||||
nullmix * [sconh(rapidity, sig)*space_part; sconh(rapidity, -sig)]
|
||||
[sconh(rapidity, sig)*space_part; sconh(rapidity, -sig)]
|
||||
end
|
||||
|
||||
rand_on_shell(rng::AbstractRNG, shells::Array{T}) where T <: Number =
|
||||
@ -39,28 +39,21 @@ rand_on_shell(shells::Array{<:Number}) = rand_on_shell(Random.default_rng(), she
|
||||
|
||||
# === elements ===
|
||||
|
||||
point(pos) = [pos; 0.5; 0.5 * dot(pos, pos)]
|
||||
|
||||
plane(normal, offset) = [-normal; 0; -offset]
|
||||
plane(normal, offset) = [normal; offset; offset]
|
||||
|
||||
function sphere(center, radius)
|
||||
dist_sq = dot(center, center)
|
||||
[
|
||||
return [
|
||||
center / radius;
|
||||
0.5 / radius;
|
||||
0.5 * (dist_sq / radius - radius)
|
||||
0.5 * ((dist_sq - 1) / radius - radius);
|
||||
0.5 * ((dist_sq + 1) / radius - radius)
|
||||
]
|
||||
end
|
||||
|
||||
# === Gram matrix realization ===
|
||||
|
||||
# basis changes
|
||||
nullmix = [Matrix{Int64}(I, 3, 3) zeros(Int64, 3, 2); zeros(Int64, 2, 3) [-1 1; 1 1]//2]
|
||||
unmix = [Matrix{Int64}(I, 3, 3) zeros(Int64, 3, 2); zeros(Int64, 2, 3) [-1 1; 1 1]]
|
||||
|
||||
# the Lorentz form
|
||||
## [old] Q = diagm([1, 1, 1, 1, -1])
|
||||
Q = [Matrix{Int64}(I, 3, 3) zeros(Int64, 3, 2); zeros(Int64, 2, 3) [0 -2; -2 0]]
|
||||
Q = diagm([1, 1, 1, 1, -1])
|
||||
|
||||
# project a matrix onto the subspace of matrices whose entries vanish at the
|
||||
# given indices
|
||||
@ -318,8 +311,7 @@ end
|
||||
# explicit entry of `gram`. use gradient descent starting from `guess`
|
||||
function realize_gram(
|
||||
gram::SparseMatrixCSC{T, <:Any},
|
||||
guess::Matrix{T},
|
||||
frozen = nothing;
|
||||
guess::Matrix{T};
|
||||
scaled_tol = 1e-30,
|
||||
min_efficiency = 0.5,
|
||||
init_rate = 1.0,
|
||||
@ -344,15 +336,6 @@ function realize_gram(
|
||||
scale_adjustment = sqrt(T(length(constrained)))
|
||||
tol = scale_adjustment * scaled_tol
|
||||
|
||||
# list the un-frozen indices
|
||||
has_frozen = !isnothing(frozen)
|
||||
if has_frozen
|
||||
is_unfrozen = fill(true, size(guess))
|
||||
is_unfrozen[frozen] .= false
|
||||
unfrozen = findall(is_unfrozen)
|
||||
unfrozen_stacked = reshape(is_unfrozen, total_dim)
|
||||
end
|
||||
|
||||
# initialize variables
|
||||
grad_rate = init_rate
|
||||
L = copy(guess)
|
||||
@ -388,23 +371,7 @@ function realize_gram(
|
||||
if min_eigval <= 0
|
||||
hess -= reg_scale * min_eigval * I
|
||||
end
|
||||
|
||||
# compute the Newton step
|
||||
neg_grad_stacked = reshape(neg_grad, total_dim)
|
||||
if has_frozen
|
||||
hess = hess[unfrozen_stacked, unfrozen_stacked]
|
||||
neg_grad_compressed = neg_grad_stacked[unfrozen_stacked]
|
||||
else
|
||||
neg_grad_compressed = neg_grad_stacked
|
||||
end
|
||||
base_step_compressed = hess \ neg_grad_compressed
|
||||
if has_frozen
|
||||
base_step_stacked = zeros(total_dim)
|
||||
base_step_stacked[unfrozen_stacked] .= base_step_compressed
|
||||
else
|
||||
base_step_stacked = base_step_compressed
|
||||
end
|
||||
base_step = reshape(base_step_stacked, dims)
|
||||
base_step = reshape(hess \ reshape(neg_grad, total_dim), dims)
|
||||
push!(history.base_step, base_step)
|
||||
|
||||
# store the current position, loss, and slope
|
||||
@ -445,7 +412,7 @@ function realize_gram(
|
||||
|
||||
# return the factorization and its history
|
||||
push!(history.scaled_loss, loss / scale_adjustment)
|
||||
L, loss < tol, history
|
||||
L, true, history
|
||||
end
|
||||
|
||||
end
|
@ -1,7 +1,6 @@
|
||||
include("Engine.jl")
|
||||
|
||||
using SparseArrays
|
||||
using Random
|
||||
|
||||
# initialize the partial gram matrix for a sphere inscribed in a regular
|
||||
# tetrahedron
|
||||
@ -11,23 +10,23 @@ values = BigFloat[]
|
||||
for j in 1:9
|
||||
for k in 1:9
|
||||
filled = false
|
||||
if j == 9
|
||||
if k <= 5 && k != 2
|
||||
if j == k
|
||||
push!(values, j < 9 ? 1 : 0)
|
||||
filled = true
|
||||
elseif (j == 9)
|
||||
if (k <= 5 && k != 2)
|
||||
push!(values, 0)
|
||||
filled = true
|
||||
end
|
||||
elseif k == 9
|
||||
if j <= 5 && j != 2
|
||||
elseif (k == 9)
|
||||
if (j <= 5 && j != 2)
|
||||
push!(values, 0)
|
||||
filled = true
|
||||
end
|
||||
elseif j == k
|
||||
push!(values, 1)
|
||||
filled = true
|
||||
elseif j == 1 || k == 1
|
||||
elseif (j == 1 || k == 1)
|
||||
push!(values, 0)
|
||||
filled = true
|
||||
elseif j == 2 || k == 2
|
||||
elseif (j == 2 || k == 2)
|
||||
push!(values, -1)
|
||||
filled = true
|
||||
end
|
||||
@ -47,26 +46,59 @@ append!(values, fill(-0.5, 4))
|
||||
=#
|
||||
gram = sparse(J, K, values)
|
||||
|
||||
# set initial guess (random)
|
||||
## Random.seed!(58271) # stuck; step size collapses on step 48
|
||||
## Random.seed!(58272) # good convergence
|
||||
## Random.seed!(58273) # stuck; step size collapses on step 18
|
||||
## Random.seed!(58274) # stuck
|
||||
## Random.seed!(58275) #
|
||||
## guess = Engine.rand_on_shell(fill(BigFloat(-1), 8))
|
||||
|
||||
# set initial guess
|
||||
Random.seed!(58271)
|
||||
guess = hcat(
|
||||
Engine.plane(BigFloat[0, 0, 1], BigFloat(0)),
|
||||
Engine.sphere(BigFloat[0, 0, 0], BigFloat(1//2)) + 0.1*Engine.rand_on_shell([BigFloat(-1)]),
|
||||
Engine.plane(-BigFloat[1, 0, 0], BigFloat(-1)) + 0.1*Engine.rand_on_shell([BigFloat(-1)]),
|
||||
Engine.plane(-BigFloat[cos(2pi/3), sin(2pi/3), 0], BigFloat(-1)) + 0.1*Engine.rand_on_shell([BigFloat(-1)]),
|
||||
Engine.plane(-BigFloat[cos(-2pi/3), sin(-2pi/3), 0], BigFloat(-1)) + 0.1*Engine.rand_on_shell([BigFloat(-1)]),
|
||||
Engine.sphere(BigFloat[-1, 0, 0], BigFloat(1//5)) + 0.1*Engine.rand_on_shell([BigFloat(-1)]),
|
||||
Engine.sphere(BigFloat[cos(-pi/3), sin(-pi/3), 0], BigFloat(1//5)) + 0.1*Engine.rand_on_shell([BigFloat(-1)]),
|
||||
Engine.sphere(BigFloat[cos(pi/3), sin(pi/3), 0], BigFloat(1//5)) + 0.1*Engine.rand_on_shell([BigFloat(-1)]),
|
||||
BigFloat[0, 0, 0, 0, 1]
|
||||
Engine.sphere(BigFloat[0, 0, 0], BigFloat(1//2)),
|
||||
Engine.plane(BigFloat[1, 0, 0], BigFloat(1)),
|
||||
Engine.plane(BigFloat[cos(2pi/3), sin(2pi/3), 0], BigFloat(1)),
|
||||
Engine.plane(BigFloat[cos(-2pi/3), sin(-2pi/3), 0], BigFloat(1)),
|
||||
Engine.sphere(BigFloat[-1, 0, 0], BigFloat(1//5)),
|
||||
Engine.sphere(BigFloat[cos(-pi/3), sin(-pi/3), 0], BigFloat(1//5)),
|
||||
Engine.sphere(BigFloat[cos(pi/3), sin(pi/3), 0], BigFloat(1//5)),
|
||||
BigFloat[0, 0, 0, 1, 1]
|
||||
)
|
||||
frozen = [CartesianIndex(j, 9) for j in 1:5]
|
||||
#=
|
||||
guess = hcat(
|
||||
Engine.plane(BigFloat[0, 0, 1], BigFloat(0)),
|
||||
Engine.sphere(BigFloat[0, 0, 0], BigFloat(0.9)),
|
||||
Engine.plane(BigFloat[1, 0, 0], BigFloat(1)),
|
||||
Engine.plane(BigFloat[cos(2pi/3), sin(2pi/3), 0], BigFloat(1)),
|
||||
Engine.plane(BigFloat[cos(-2pi/3), sin(-2pi/3), 0], BigFloat(1)),
|
||||
Engine.sphere(4//3*BigFloat[-1, 0, 0], BigFloat(1//3)),
|
||||
Engine.sphere(4//3*BigFloat[cos(-pi/3), sin(-pi/3), 0], BigFloat(1//3)),
|
||||
Engine.sphere(4//3*BigFloat[cos(pi/3), sin(pi/3), 0], BigFloat(1//3)),
|
||||
BigFloat[0, 0, 0, 1, 1]
|
||||
)
|
||||
=#
|
||||
|
||||
# complete the gram matrix using Newton's method with backtracking
|
||||
L, success, history = Engine.realize_gram(gram, guess, frozen)
|
||||
# complete the gram matrix using gradient descent followed by Newton's method
|
||||
#=
|
||||
L, history = Engine.realize_gram_gradient(gram, guess, scaled_tol = 0.01)
|
||||
L_pol, history_pol = Engine.realize_gram_newton(gram, L, rate = 0.3, scaled_tol = 1e-9)
|
||||
L_pol2, history_pol2 = Engine.realize_gram_newton(gram, L_pol)
|
||||
=#
|
||||
L, success, history = Engine.realize_gram(gram, guess)
|
||||
completed_gram = L'*Engine.Q*L
|
||||
println("Completed Gram matrix:\n")
|
||||
display(completed_gram)
|
||||
#=
|
||||
println(
|
||||
"\nSteps: ",
|
||||
size(history.scaled_loss, 1),
|
||||
" + ", size(history_pol.scaled_loss, 1),
|
||||
" + ", size(history_pol2.scaled_loss, 1)
|
||||
)
|
||||
println("Loss: ", history_pol2.scaled_loss[end], "\n")
|
||||
=#
|
||||
if success
|
||||
println("\nTarget accuracy achieved!")
|
||||
else
|
||||
|
@ -1,77 +0,0 @@
|
||||
include("Engine.jl")
|
||||
|
||||
using SparseArrays
|
||||
|
||||
# this problem is from a sangaku by Irisawa Shintarō Hiroatsu. the article below
|
||||
# includes a nice translation of the problem statement, which was recorded in
|
||||
# Uchida Itsumi's book _Kokon sankan_ (_Mathematics, Past and Present_)
|
||||
#
|
||||
# "Japan's 'Wasan' Mathematical Tradition", by Abe Haruki
|
||||
# https://www.nippon.com/en/japan-topics/c12801/
|
||||
#
|
||||
|
||||
# initialize the partial gram matrix
|
||||
J = Int64[]
|
||||
K = Int64[]
|
||||
values = BigFloat[]
|
||||
for s in 1:9
|
||||
# each sphere is represented by a spacelike vector
|
||||
push!(J, s)
|
||||
push!(K, s)
|
||||
push!(values, 1)
|
||||
|
||||
# the circumscribing sphere is internally tangent to all of the other spheres
|
||||
if s > 1
|
||||
append!(J, [1, s])
|
||||
append!(K, [s, 1])
|
||||
append!(values, [1, 1])
|
||||
end
|
||||
|
||||
if s > 3
|
||||
# each chain sphere is externally tangent to the "sun" and "moon" spheres
|
||||
for n in 2:3
|
||||
append!(J, [s, n])
|
||||
append!(K, [n, s])
|
||||
append!(values, [-1, -1])
|
||||
end
|
||||
|
||||
# each chain sphere is externally tangent to the next chain sphere
|
||||
s_next = 4 + mod(s-3, 6)
|
||||
append!(J, [s, s_next])
|
||||
append!(K, [s_next, s])
|
||||
append!(values, [-1, -1])
|
||||
end
|
||||
end
|
||||
gram = sparse(J, K, values)
|
||||
|
||||
# make an initial guess
|
||||
guess = hcat(
|
||||
Engine.sphere(BigFloat[0, 0, 0], BigFloat(15)),
|
||||
Engine.sphere(BigFloat[0, 0, -9], BigFloat(5)),
|
||||
Engine.sphere(BigFloat[0, 0, 11], BigFloat(3)),
|
||||
(
|
||||
Engine.sphere(9*BigFloat[cos(k*π/3), sin(k*π/3), 0], BigFloat(2.5))
|
||||
for k in 1:6
|
||||
)...
|
||||
)
|
||||
frozen = [CartesianIndex(4, k) for k in 1:4]
|
||||
|
||||
# complete the gram matrix using Newton's method with backtracking
|
||||
L, success, history = Engine.realize_gram(gram, guess, frozen)
|
||||
completed_gram = L'*Engine.Q*L
|
||||
println("Completed Gram matrix:\n")
|
||||
display(completed_gram)
|
||||
if success
|
||||
println("\nTarget accuracy achieved!")
|
||||
else
|
||||
println("\nFailed to reach target accuracy")
|
||||
end
|
||||
println("Steps: ", size(history.scaled_loss, 1))
|
||||
println("Loss: ", history.scaled_loss[end], "\n")
|
||||
if success
|
||||
println("Chain diameters:")
|
||||
println(" ", 1 / L[4,4], " sun (given)")
|
||||
for k in 5:9
|
||||
println(" ", 1 / L[4,k], " sun")
|
||||
end
|
||||
end
|
@ -23,6 +23,12 @@ end
|
||||
gram = sparse(J, K, values)
|
||||
|
||||
# set the independent variable
|
||||
#
|
||||
# using gram[6, 2] or gram[7, 1] as the independent variable seems to stall
|
||||
# convergence, even if its value comes from a known solution, like
|
||||
#
|
||||
# gram[6, 2] = 0.9936131705272925
|
||||
#
|
||||
indep_val = -9//5
|
||||
gram[6, 1] = BigFloat(indep_val)
|
||||
gram[1, 6] = gram[6, 1]
|
||||
@ -30,25 +36,30 @@ gram[1, 6] = gram[6, 1]
|
||||
# in this initial guess, the mutual tangency condition is satisfied for spheres
|
||||
# 1 through 5
|
||||
Random.seed!(50793)
|
||||
guess = let
|
||||
a = sqrt(BigFloat(3)/2)
|
||||
hcat(
|
||||
guess = hcat(
|
||||
sqrt(1/BigFloat(2)) * BigFloat[
|
||||
1 1 -1 -1 0
|
||||
1 -1 1 -1 0
|
||||
1 -1 -1 1 0
|
||||
0.5 0.5 0.5 0.5 1+a
|
||||
0.5 0.5 0.5 0.5 1-a
|
||||
1 1 -1 -1 0;
|
||||
1 -1 1 -1 0;
|
||||
1 -1 -1 1 0;
|
||||
0 0 0 0 -sqrt(BigFloat(6));
|
||||
1 1 1 1 2;
|
||||
] + 0.2*Engine.rand_on_shell(fill(BigFloat(-1), 5)),
|
||||
Engine.rand_on_shell(fill(BigFloat(-1), 2))
|
||||
)
|
||||
end
|
||||
|
||||
# complete the gram matrix using Newton's method with backtracking
|
||||
# complete the gram matrix
|
||||
#=
|
||||
L, history = Engine.realize_gram_gradient(gram, guess, scaled_tol = 0.01)
|
||||
L_pol, history_pol = Engine.realize_gram_newton(gram, L)
|
||||
=#
|
||||
L, success, history = Engine.realize_gram(gram, guess)
|
||||
completed_gram = L'*Engine.Q*L
|
||||
println("Completed Gram matrix:\n")
|
||||
display(completed_gram)
|
||||
#=
|
||||
println("\nSteps: ", size(history.scaled_loss, 1), " + ", size(history_pol.scaled_loss, 1))
|
||||
println("Loss: ", history_pol.scaled_loss[end], "\n")
|
||||
=#
|
||||
if success
|
||||
println("\nTarget accuracy achieved!")
|
||||
else
|
||||
|
@ -8,32 +8,16 @@ using Random
|
||||
J = Int64[]
|
||||
K = Int64[]
|
||||
values = BigFloat[]
|
||||
for j in 1:6
|
||||
for k in 1:6
|
||||
filled = false
|
||||
if j == 6
|
||||
if k <= 4
|
||||
push!(values, 0)
|
||||
filled = true
|
||||
end
|
||||
elseif k == 6
|
||||
if j <= 4
|
||||
push!(values, 0)
|
||||
filled = true
|
||||
end
|
||||
elseif j == k
|
||||
push!(values, 1)
|
||||
filled = true
|
||||
elseif j <= 4 && k <= 4
|
||||
push!(values, -1/BigFloat(3))
|
||||
filled = true
|
||||
else
|
||||
push!(values, -1)
|
||||
filled = true
|
||||
end
|
||||
if filled
|
||||
for j in 1:5
|
||||
for k in 1:5
|
||||
push!(J, j)
|
||||
push!(K, k)
|
||||
if j == k
|
||||
push!(values, 1)
|
||||
elseif (j <= 4 && k <= 4)
|
||||
push!(values, -1/BigFloat(3))
|
||||
else
|
||||
push!(values, -1)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -41,20 +25,19 @@ gram = sparse(J, K, values)
|
||||
|
||||
# set initial guess
|
||||
Random.seed!(99230)
|
||||
guess = hcat(
|
||||
sqrt(1/BigFloat(3)) * BigFloat[
|
||||
guess = sqrt(1/BigFloat(3)) * BigFloat[
|
||||
1 1 -1 -1 0
|
||||
1 -1 1 -1 0
|
||||
1 -1 -1 1 0
|
||||
0 0 0 0 1.5
|
||||
1 1 1 1 -0.5
|
||||
] + 0.2*Engine.rand_on_shell(fill(BigFloat(-1), 5)),
|
||||
BigFloat[0, 0, 0, 0, 1]
|
||||
)
|
||||
frozen = [CartesianIndex(j, 6) for j in 1:5]
|
||||
1 1 1 1 -2
|
||||
1 1 1 1 1
|
||||
] + 0.2*Engine.rand_on_shell(fill(BigFloat(-1), 5))
|
||||
|
||||
# complete the gram matrix using Newton's method with backtracking
|
||||
L, success, history = Engine.realize_gram(gram, guess, frozen)
|
||||
# complete the gram matrix
|
||||
#=
|
||||
L, history = Engine.realize_gram_newton(gram, guess)
|
||||
=#
|
||||
L, success, history = Engine.realize_gram(gram, guess)
|
||||
completed_gram = L'*Engine.Q*L
|
||||
println("Completed Gram matrix:\n")
|
||||
display(completed_gram)
|
||||
|
@ -1,96 +0,0 @@
|
||||
include("Engine.jl")
|
||||
|
||||
using LinearAlgebra
|
||||
using SparseArrays
|
||||
using Random
|
||||
|
||||
# initialize the partial gram matrix for a sphere inscribed in a regular
|
||||
# tetrahedron
|
||||
J = Int64[]
|
||||
K = Int64[]
|
||||
values = BigFloat[]
|
||||
for j in 1:11
|
||||
for k in 1:11
|
||||
filled = false
|
||||
if j == 11
|
||||
if k <= 4
|
||||
push!(values, 0)
|
||||
filled = true
|
||||
end
|
||||
elseif k == 11
|
||||
if j <= 4
|
||||
push!(values, 0)
|
||||
filled = true
|
||||
end
|
||||
elseif j == k
|
||||
push!(values, j <= 6 ? 1 : 0)
|
||||
filled = true
|
||||
elseif j <= 4
|
||||
if k <= 4
|
||||
push!(values, -1/BigFloat(3))
|
||||
filled = true
|
||||
elseif k == 5
|
||||
push!(values, -1)
|
||||
filled = true
|
||||
elseif 7 <= k <= 10 && k - j != 6
|
||||
push!(values, 0)
|
||||
filled = true
|
||||
end
|
||||
elseif k <= 4
|
||||
if j == 5
|
||||
push!(values, -1)
|
||||
filled = true
|
||||
elseif 7 <= j <= 10 && j - k != 6
|
||||
push!(values, 0)
|
||||
filled = true
|
||||
end
|
||||
elseif j == 6 && 7 <= k <= 10 || k == 6 && 7 <= j <= 10
|
||||
push!(values, 0)
|
||||
filled = true
|
||||
end
|
||||
if filled
|
||||
push!(J, j)
|
||||
push!(K, k)
|
||||
end
|
||||
end
|
||||
end
|
||||
gram = sparse(J, K, values)
|
||||
|
||||
# set initial guess
|
||||
Random.seed!(99230)
|
||||
guess = hcat(
|
||||
sqrt(1/BigFloat(3)) * BigFloat[
|
||||
1 1 -1 -1 0 0
|
||||
1 -1 1 -1 0 0
|
||||
1 -1 -1 1 0 0
|
||||
0 0 0 0 1.5 0.5
|
||||
1 1 1 1 -0.5 -1.5
|
||||
] + 0.0*Engine.rand_on_shell(fill(BigFloat(-1), 6)),
|
||||
Engine.point([-0.5, -0.5, -0.5] + 0.3*randn(3)),
|
||||
Engine.point([-0.5, 0.5, 0.5] + 0.3*randn(3)),
|
||||
Engine.point([ 0.5, -0.5, 0.5] + 0.3*randn(3)),
|
||||
Engine.point([ 0.5, 0.5, -0.5] + 0.3*randn(3)),
|
||||
BigFloat[0, 0, 0, 0, 1]
|
||||
)
|
||||
frozen = vcat(
|
||||
[CartesianIndex(4, k) for k in 7:10],
|
||||
[CartesianIndex(j, 11) for j in 1:5]
|
||||
)
|
||||
|
||||
# complete the gram matrix using Newton's method with backtracking
|
||||
L, success, history = Engine.realize_gram(gram, guess, frozen)
|
||||
completed_gram = L'*Engine.Q*L
|
||||
println("Completed Gram matrix:\n")
|
||||
display(completed_gram)
|
||||
if success
|
||||
println("\nTarget accuracy achieved!")
|
||||
else
|
||||
println("\nFailed to reach target accuracy")
|
||||
end
|
||||
println("Steps: ", size(history.scaled_loss, 1))
|
||||
println("Loss: ", history.scaled_loss[end])
|
||||
if success
|
||||
infty = BigFloat[0, 0, 0, 0, 1]
|
||||
radius_ratio = dot(infty, Engine.Q * L[:,5]) / dot(infty, Engine.Q * L[:,6])
|
||||
println("\nCircumradius / inradius: ", radius_ratio)
|
||||
end
|
@ -6,22 +6,22 @@ These coordinates are of form $I=(c, r, x, y, z)$ where we think of $c$ as the c
|
||||
|
||||
| Entity or Relationship | Representation | Comments/questions |
|
||||
| ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Sphere s with radius r>0 centered on P = (x,y,z) | $I_s = (1/c, 1/r, x/r, y/r, z/r)$ satisfying $Q(I_s,I_s) = -1$, i.e., $c = r/(\|P\|^2 - r^2)$. | Can also write $I_s = (\|P\|^2/r - r, 1/r, x/r. y/r, z/r)$—so there is no trouble if $\|E_{I_s}\| = r$, just get first coordinate to be 0. |
|
||||
| Plane p with unit normal (x,y,z) through the point s(x,y,z) | $I_p = (-2s, 0, -x, -y, -z)$ | Note $Q(I_p, I_p)$ is still −1. |
|
||||
| Sphere s with radius r>0 centered on P = (x,y,z) | $I_s = (1/c, 1/r, x/r, y/r, z/r)$ satisfying $Q(I_s,I_s) = -1$, i.e., $c = r/(\|P\|^2 - r^2)$. | Can also write $I_s = (\|P\|^2/r - r, 1/r, x/r. y/r, z/r)$ -- so there is no trouble if $\|E_{I_s}\| = r$, just get first coordinate to be 0. |
|
||||
| Plane p with unit normal (x,y,z), a distance s from origin | $I_p = (2s, 0, x, y, z)$ | Note $Q(I_p, I_p)$ is still -1. Also, there are two representations for each plane through the origin, namely $(0,0,x,y,z)$ and $(0,0,-x,-y,-z)$ |
|
||||
| Point P with Euclidean coordinates (x,y,z) | $I_P = (\|P\|^2, 1, x, y, z)$ | Note $Q(I_P,I_P) = 0$. Because of this we might choose some other scaling of the inversive coordinates, say $(\||P\||,1/\||P\||,x/\||P\||,y/\||P\||,z/\||P\||)$ instead, but that fails at the origin, and likely won't have some of the other nice properties listed below. Note that scaling just the co-radius by $s$ and the radius by $1/s$ (which still preserves $Q=0$) dilates by a factor of $s$ about the origin, so that $(\|P\|, \|P\|, x, y, z)$, which might look symmetric, would actually have to represent the Euclidean point $(x/\||P\||, y/\||P\||, z/\||P\||)$ . |
|
||||
| ∞, the "point at infinity" | $I_\infty = (1,0,0,0,0)$ | The only solution to $Q(I,I) = 0$ not covered by the above case. |
|
||||
| P lies on sphere or plane given by I | $Q(I_P, I) = 0$ | |
|
||||
| Sphere/planes represented by I and J are tangent | If $I$ and $J$ have the same orientation where they touch, $Q(I,J) = -1$. If they have opposing orientations, $Q(I,J) = 1$. | For example, the $xy$ plane with normal $-e_z$, represented by $(0,0,0,0,1)$, is tangent with matching orientation to the unit sphere centered at $(0,0,1)$ with outward normals, represented by $(0,1,0,0,1)$. Accordingly, their $Q$-product is −1. |
|
||||
| Sphere/planes represented by I and J are tangent | $Q(I,J) = 1$ (??, see note at right) | Seems as though this must be $Q(I,J) = \pm1$ ? For example, the $xy$ plane represented by (0,0,0,0,1) is tangent to the unit circle centered at (0,0,1) rep'd by (0,1,0,0,1), but their Q-product is -1. And in general you can reflect any sphere tangent to any plane through the plane and it should flip the sign of $Q(I,J)$, if I am not mistaken. |
|
||||
| Sphere/planes represented by I and J intersect (respectively, don't intersect) | $\|Q(I,J)\| < (\text{resp. }>)\; 1$ | Follows from the angle formula, at least conceptually. |
|
||||
| P is center of sphere represented by I | Well, $Q(I_P, I)$ comes out to be $(\|P\|^2/r - r + \|P\|^2/r)/2 - \|P\|^2/r$ or just $-r/2$ . | Is it if and only if ? No this probably doesn't work because center is not conformal quantity. |
|
||||
| Distance between P and R is d | $Q(I_P, I_R) = d^2/2$ | |
|
||||
| Distance between P and sphere/plane rep by I | | In the very simple case of a plane $I$ rep'd by $(2s, 0, x, y, z)$ and a point $P$ that lies on its perpendicular through the origin, rep'd by $(r^2, 1, rx, ry, rz)$ we get $Q(I, I_p) = s-r$, which is indeed the signed distance between $I$ and $P$. Not sure if this generalizes to other combinations? |
|
||||
| Distance between sphere/planes rep by I and J | Note that for any two Euclidean-concentric spheres rep by $I$ and $J$ with radius $r$ and $s,$ $Q(I,J) = -\frac12\left(\frac rs + \frac sr\right)$ depends only on the ratio of $r$ and $s$. So this can't give something that determines the Euclidean distance between the two spheres, which presumably grows as the two spheres are blown up proportionally. For another example, for any two parallel planes, $Q(I,J) = \pm1$. | Alex had said: $Q(I,J)=\cosh(d/2)^2$ maybe where d is distance in usual hyperbolic metric. Or maybe $\cosh(d)$. That may be right depending on what's meant by the hyperbolic metric there, but it seems like it won't determine a reasonable Euclidean distance between planes, which should differ between different pairs of parallel planes. |
|
||||
| Distance between sphere/planes rep by I and J | Note that for any two Euclidean-concentric spheres rep by $I$ and $J$ with radius $r$ and $s,$ $Q(I,J) = -\frac12\left(\frac rs + \frac sr\right)$ depends only on the ratio of $r$ and $s$. So this can't give something that determines the Euclidean distance between the two spheres, which presumably grows as the two spheres are blown up proportionally. For another example, for any two parallel planes, $Q(I,J) = \pm1$. | Alex had said: Q(I,J)=cosh^2 (d/2) maybe where d is distance in usual hyperbolic metric. Or maybe cosh d. That may be right depending on what's meant by the hyperbolic metric there, but it seems like it won't determine a reasonable Euclidean distance between planes, which should differ between different pairs of parallel planes. |
|
||||
| Sphere centered on P through R | | Probably just calculate distance etc. |
|
||||
| Plane rep'd by I goes through center of sphere rep'd by J | I think this is equivalent to the plane being perpendicular to the sphere, i.e.$Q(I,J) = 0$. | |
|
||||
| Dihedral angle between planes (or spheres?) rep by I and J | $\theta = \arccos(Q(I,J))$ | Aaron Fenyes points out: The angle between spheres in $S^3$ matches the angle between the planes they bound in $R^{(1,4)}$, which matches the angle between the spacelike vectors perpendicular to those planes. So we should have $Q(I,J) = \cos(\theta)$. Note that when the spheres do not intersect, we can interpret this as the "imaginary angle" between them, via $\cosh(t) = \cos(it)$. |
|
||||
| R, P, S are collinear | Maybe just cross product of two differences is 0. Or, $R,P,S,\infty$ lie on a circle, or equivalently, $I_R,I_P,I_S,I_\infty$ span a plane (rather than a three-space). | $R,P,S$ lying on a line isn't a conformal property, but $R,P,S,\infty$ lying on a circle is. |
|
||||
| Plane through noncollinear R, P, S | Should be, just solve $Q(I, I_R) = 0$ etc. | |
|
||||
| Dihedral angle between planes (or spheres?) rep by I and J | $\theta = \arccos(Q(I,J))$ | Aaron Fenyes points out: The angle between spheres in $S^3$ matches the angle between the planes they bound in $R^{(1,4)}$, which matches the angle between the spacelike vectors perpendicular to those planes. So we should have $Q(I,J) = \cos\theta$. Note that when the spheres do not intersect, we can interpret this as the "imaginary angle" between them, via $\cosh t = \cos it$. |
|
||||
| R, P, S are collinear | Maybe just cross product of two differences is 0. Or, $R,P,S,\infty$ lie on a circle, or equivalently, $I_R,I_P,I_S,I_\infty$ span a plane (rather than a three-space). | Not a conformal property, but $R,P,S,\infty$ lying on a circle _is_. |
|
||||
| Plane through noncollinear R, P, S | Should be, just solve Q(I, I_R) = 0 etc. | |
|
||||
| circle | Maybe concentric sphere and the containing plane? Note it is easy to constrain the relationship between those two: they must be perpendicular. | Defn: circle is intersection of two spheres. That does cover lines. But you lose the canonicalness |
|
||||
| line | Maybe two perpendicular containing planes? Maybe the plane perpendicular to the line and through origin, together with the point of the line on that plane? Or maybe just as a bag of collinear points? | The first is the limiting case of the possible circle rep, but it is not canonical. The second appears to be canonical, but I don't see a circle rep that corresponds to it. |
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user