Skip to content

Commit

Permalink
Add tests for gradient checking
Browse files Browse the repository at this point in the history
  • Loading branch information
avik-pal committed May 9, 2019
1 parent d6b9112 commit 87a2aac
Show file tree
Hide file tree
Showing 13 changed files with 163 additions and 26 deletions.
1 change: 1 addition & 0 deletions .codecov.yml
@@ -0,0 +1 @@
comment: false
23 changes: 23 additions & 0 deletions .travis.yml
@@ -0,0 +1,23 @@
# Documentation: http://docs.travis-ci.com/user/languages/julia/
language: julia

os:
- linux
- osx

julia:
- 1.0
- 1.1
- nightly

matrix:
allow_failures:
- julia: nightly

script:
- julia --color=yes -e 'using Pkg; Pkg.activate(); Pkg.instantiate(); Pkg.test()'

after_success:
- if [[ $TRAVIS_JULIA_VERSION = 1.1 ]] && [[ $TRAVIS_OS_NAME = linux ]]; then
julia -e 'using Pkg; Pkg.add("Coverage"); using Coverage; Codecov.submit(process_folder())';
fi
4 changes: 3 additions & 1 deletion Manifest.toml
Expand Up @@ -317,6 +317,8 @@ version = "0.8.1"

[[Zygote]]
deps = ["DiffRules", "ForwardDiff", "IRTools", "InteractiveUtils", "LinearAlgebra", "MacroTools", "NNlib", "NaNMath", "Random", "Requires", "SpecialFunctions", "Statistics"]
git-tree-sha1 = "7f31d606965a9c37f6ddf06c9082b17370e0eb7e"
git-tree-sha1 = "ac4a5b2d7a6f2b2925a64150d46479a558c26667"
repo-rev = "master"
repo-url = "https://github.com/FluxML/Zygote.jl.git"
uuid = "e88e6eb3-aa80-5325-afca-941959d7151f"
version = "0.3.0"
9 changes: 8 additions & 1 deletion Project.toml
Expand Up @@ -4,6 +4,13 @@ authors = ["Avik Pal <avikpal@iitk.ac.in>"]
version = "0.1.0"

[deps]
Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b"
Flux = "587475ba-b771-5e3f-ad9e-33799f191a9c"
Requires = "ae029012-a4dd-5104-9daa-d747884805df"

[extras]
Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f"

[targets]
test = ["Test", "Zygote"]
2 changes: 1 addition & 1 deletion src/RayTracer.jl
Expand Up @@ -5,7 +5,7 @@ using Requires
export raytrace
export Vec3, rgb
export SimpleSphere, CheckeredSphere, SimpleCylinder, CheckeredCylinder,
Triangle, Disc
Sphere, Cylinder, Triangle, Disc
export clip01
export get_primary_rays
export DistantLight, PointLight
Expand Down
2 changes: 1 addition & 1 deletion src/objects.jl
Expand Up @@ -27,7 +27,7 @@ include("objects/disc.jl")
function light(s::S, origin, direction, dist, lgt::L, eye_pos,
scene, obj_num, bounce) where {S<:Object, L<:Light}
pt = origin + direction * dist
normal = get_normal(s, pt, direction)
normal = get_normal(s, pt)
dir_light, intensity = get_shading_info(lgt, pt)
dir_origin = normalize(eye_pos - pt)
nudged = pt + normal * 0.0001f0 # Nudged to miss itself
Expand Down
3 changes: 1 addition & 2 deletions src/objects/cylinder.jl
Expand Up @@ -107,8 +107,7 @@ function intersect(cy::Cylinder, origin, direction)
return result
end

function get_normal(c::Cylinder, pt, direction)
# TODO: Handle normal for inner surface
function get_normal(c::Cylinder, pt)
pt_c = pt - c.center
return normalize(pt_c - dot(pt_c, c.axis) * c.axis)
end
27 changes: 9 additions & 18 deletions src/objects/disc.jl
Expand Up @@ -4,23 +4,13 @@ import Base.setproperty!
# - Disc - #
# -------- #

# TODO: Use barycentric coordinates and Moller-Trumbore Algorithm
mutable struct Disc{V,T<:Real} <: Object
center::Vec3{V}
normal::Vec3{V}
normal::Vec3{V} # This needs to be normalized everytime before usage
radius::T
material::Material
end

# Will mess with gradients. Need an alternate solution
function setproperty!(d::Disc, sym::Symbol, x::Vec3)
if sym == :normal
setfield!(d, :normal, normalize(x))
else
setfield!(d, sym, x)
end
end

@diffops Disc

# The next 3 functions are just convenience functions for handling
Expand Down Expand Up @@ -53,9 +43,10 @@ function Disc(c::Vec3, n::Vec3, r::T; color = rgb(0.5f0), reflection = 0.5f0) wh
end

function intersect(d::Disc, origin, direction)
dot_dn = dot(direction, d.normal)
normal = normalize(d.normal)
dot_dn = dot(direction, normal)
p_org = d.center - origin
t = dot(p_org, d.normal) ./ dot_dn
t = dot(p_org, normal) ./ dot_dn
pt = origin + direction * t
dist = l2norm(pt - d.center)
r2 = d.radius ^ 2
Expand All @@ -70,9 +61,9 @@ function intersect(d::Disc, origin, direction)
return result
end

function get_normal(t::Disc, pt, direction)
normal = Vec3(repeat(t.normal.x, inner = size(pt.x)),
repeat(t.normal.y, inner = size(pt.y)),
repeat(t.normal.z, inner = size(pt.z)))
return normal
function get_normal(d::Disc, pt)
normal = normalize(d.normal)
return Vec3(repeat(normal.x, inner = size(pt.x)),
repeat(normal.y, inner = size(pt.y)),
repeat(normal.z, inner = size(pt.z)))
end
2 changes: 1 addition & 1 deletion src/objects/sphere.jl
Expand Up @@ -51,7 +51,7 @@ function intersect(s::Sphere, origin, direction)
return result
end

get_normal(s::Sphere, pt, direction) = normalize(pt - s.center)
get_normal(s::Sphere, pt) = normalize(pt - s.center)

# ---------------------- #
# -- Helper Functions -- #
Expand Down
2 changes: 1 addition & 1 deletion src/objects/triangle.jl
Expand Up @@ -56,7 +56,7 @@ function intersect(t::Triangle, origin, direction)
return result
end

function get_normal(t::Triangle, pt, direction)
function get_normal(t::Triangle, pt)
# normal not expanded
normal_nexp = normalize(cross(t.v2 - t.v1, t.v3 - t.v1))
normal = Vec3(repeat(normal_nexp.x, inner = size(pt.x)),
Expand Down
105 changes: 105 additions & 0 deletions test/gradcheck.jl
@@ -0,0 +1,105 @@
using Zygote

# Taken from Zygote.jl
function ngradient(f, xs::AbstractArray...)
grads = zero.(xs)
for (x, Δ) in zip(xs, grads), i in 1:length(x)
δ = sqrt(eps())
tmp = x[i]
x[i] = tmp - δ/2
y1 = f(xs...)
x[i] = tmp + δ/2
y2 = f(xs...)
x[i] = tmp
Δ[i] = (y2-y1)/δ
end
return grads
end

get_params(x::T) where {T<:AbstractArray} = x

get_params(x::T) where {T<:Real} = [x]

get_params(x::T) where {T} = foldl((a, b) -> [a; b],
[map(i -> get_params(getfield(x, i)), fieldnames(T))...])

function set_params(s::Triangle, x::AbstractArray)
s.v1 = Vec3(x[1], x[2], x[3])
s.v2 = Vec3(x[4], x[5], x[6])
s.v3 = Vec3(x[7], x[8], x[9])
return s
end

function set_params(s::Sphere, x::AbstractArray)
s.center = Vec3(x[1], x[2], x[3])
s.radius = x[4]
return s
end

function scene_gradients(sc, x, color)
scene = deepcopy(sc)
set_params(scene, x)
loss_fn([scene], color)
end

function loss_fn(θ, color)
rendered_color = raytrace(origin, direction, θ, light, eye_pos, 0)
loss = sum(abs2.(rendered_color.x .- color.x) .+
abs2.(rendered_color.y .- color.y) .+
abs2.(rendered_color.z .- color.z))
return loss
end

# Define the Scene Parameters

screen_size = (w = 400, h = 300)

light = PointLight(Vec3(1.0), 1000.0, Vec3(0.15, 0.5, -110.5))

eye_pos = Vec3(0.0, 0.0, -5.0)

origin, direction = get_primary_rays(Float64, screen_size.w, screen_size.h, 60, eye_pos);

@testset "Triangle" begin

scene = [
Triangle(Vec3(-1.7, 1.0, 0.0), Vec3(1.0, 1.0, 0.0), Vec3(1.0, -1.0, 0.0),
color = rgb(1.0, 1.0, 1.0), reflection = 0.5)
]

scene_new = [
Triangle(Vec3(-1.9, 1.3, 0.1), Vec3(1.2, 1.1, 0.3), Vec3(0.8, -1.2, -0.15),
color = rgb(1.0, 1.0, 1.0), reflection = 0.5)
]

color = raytrace(origin, direction, scene, light, eye_pos, 0)

zygote_grads = get_params(gradient(x -> loss_fn(x, color), scene_new)[1][1])

numerical_grads = ngradient(x -> scene_gradients(scene_new[1], x, color), get_params(scene_new[1]))[1]

# Ignore the Material Gradients
@test isapprox(numerical_grads[1:end-4], zygote_grads[1:end-4], rtol = 1.0e-5)

end

@testset "Sphere" begin

scene = [
SimpleSphere(Vec3(-1.7, 1.0, 0.0), 0.6, color = rgb(1.0, 1.0, 1.0), reflection = 0.5)
]

scene_new = [
SimpleSphere(Vec3(-1.9, 1.3, 0.1), 0.9, color = rgb(1.0, 1.0, 1.0), reflection = 0.5)
]

color = raytrace(origin, direction, scene, light, eye_pos, 0)

zygote_grads = get_params(gradient(x -> loss_fn(x, color), scene_new)[1][1])

numerical_grads = ngradient(x -> scene_gradients(scene_new[1], x, color), get_params(scene_new[1]))[1]

# Ignore the Material Gradients
@test isapprox(numerical_grads[1:end-4], zygote_grads[1:end-4], rtol = 1.0e-5)

end
9 changes: 9 additions & 0 deletions test/runtests.jl
@@ -0,0 +1,9 @@
using RayTracer, Test

@testset "Differentiable Ray Tracing" begin

@testset "Gradient Checks" begin
include("gradcheck.jl")
end

end
Empty file removed tests/runtests.jl
Empty file.

0 comments on commit 87a2aac

Please sign in to comment.