Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gradient evaluation inside surfaces sometimes returns NaNs. #127

Closed
LukeP0WERS opened this issue May 31, 2024 · 6 comments
Closed

Gradient evaluation inside surfaces sometimes returns NaNs. #127

LukeP0WERS opened this issue May 31, 2024 · 6 comments

Comments

@LukeP0WERS
Copy link

I just replaced my cheap box evaluation with an exact box and immediately noticed that gradient evaluation returns NaNs anytime it is inside the surface of the object. This problem didn't happen with the previous box evaluation or my sphere evaluation and I can't seem to connect it to any specific operation. Is this just an unavoidable side effect of the auto differentiation in fidget or is it a bug? If it is an unavoidable side effect I can always just use numerical normals instead though.

@mkeeter
Copy link
Owner

mkeeter commented May 31, 2024

What's the actual equation of the shape that you're evaluating?

@LukeP0WERS
Copy link
Author

pub fn cube (pos: TreeVec3, size: [f32; 3]) -> Tree {
    let size = &TreeVec3::from_array(size);

    let q = pos.abs() - size;
    return q.max(&TreeVec3::splat(0.0)).length() + q.max_element().min(0.0);
}

@mkeeter
Copy link
Owner

mkeeter commented May 31, 2024

In general, I'd like a self-contained example that I can run, which only depends on the fidget crate, e.g. something of the form

use fidget::{vm::VmShape, context::{Context, Tree}};

fn build_tree() -> Tree {
    todo!()
}

fn main() {
    let tree = build_tree();
    let mut ctx = Context::new();
    let root = ctx.import(&tree);
    let shape = VmShape::new(&ctx, root)?;
    let tape = shape.grad_slice_tape(Default::default());

    let mut eval = VmShape::new_grad_slice_eval();
    let out = tape.eval(...)?; // add arguments here
    println!("{out:?}"); // look, there are NANs in this array!
}

Having to figure out a different crate slows things down, and a self-contained example can easily be converted into a unit test.

@LukeP0WERS
Copy link
Author

Sure this prints out [Grad { v: -1.0, dx: NaN, dy: NaN, dz: NaN }]:

use fidget::{context::{Context, Tree}, types::Grad, vm::VmShape};

fn cube(pos: [Tree; 3], size: [Tree; 3]) -> Tree {
    let q = [
        pos[0].abs() - size[0].clone(),
        pos[1].abs() - size[1].clone(),
        pos[2].abs() - size[2].clone(),
    ];
    let q_length = (
        q[0].max(0.0).square() +
        q[1].max(0.0).square() +
        q[2].max(0.0).square()
    ).sqrt();

    return q_length + q[0].clone().max(q[1].clone().max(q[2].clone())).min(0.0);
}

fn main() {
    let tree = cube(
        [
            Tree::x(),
            Tree::y(),
            Tree::z()
        ],
        [
            Tree::constant(1.0),
            Tree::constant(1.0),
            Tree::constant(1.0)
        ],
    );
    let mut ctx = Context::new();
    let root = ctx.import(&tree);
    let shape = VmShape::new(&ctx, root).unwrap();
    let tape = shape.grad_slice_tape(Default::default());

    let mut eval = VmShape::new_grad_slice_eval();
    let out = eval
        .eval(
            &tape,
            &[Grad::new(0.0, 1.0, 0.0, 0.0)],
            &[Grad::new(0.0, 0.0, 1.0, 0.0)], 
            &[Grad::new(0.0, 0.0, 0.0, 1.0)],
        )
        .unwrap();

    println!("{out:?}");
}

@mkeeter
Copy link
Owner

mkeeter commented May 31, 2024

This looks like the same issue as #15: taking derivatives of sqrt(v) at v == 0.0 is undefined, and the NaN values are propagated through the rest of the equation.

@LukeP0WERS
Copy link
Author

Ok I made the max value epsilon and it's fixed thanks 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants