theseus icon indicating copy to clipboard operation
theseus copied to clipboard

Optimize numeric jacobian for faster unit testing

Open mhmukadam opened this issue 2 years ago • 10 comments

After adding support for checking cost functions Jacobians in #321.

mhmukadam avatar Oct 07 '22 16:10 mhmukadam

Hello. Could I know where can this new function be found?

xphsean12 avatar Nov 28 '22 10:11 xphsean12

Hi @xphsean12. Are you referring the the numeric jacobians? We haven't made this optimization yet, but note that this function is only used in our unit tests. For users the recommended way is to use our analytical jacobians if you have close form expressions for the derivatives of your costs, or if not, you can use AutodiffCostFunction.

luisenp avatar Nov 28 '22 17:11 luisenp

Thanks for your advice. AutodiffCostFunction is very good, but it is a little bit slow. So I also want to specify the Jacobian to speed it up. But I am also worried about the correctness of my written one

xphsean12 avatar Nov 29 '22 03:11 xphsean12

Would this be useful for you? It's an example of how we test our analytic jacobians numerically in our unit tests. You can follow a similar template to verify the correctness of yours.

We haven't yet added the function in #321 as we have been prioritizing other features, but I'll bump its priority a bit.

luisenp avatar Nov 29 '22 12:11 luisenp

Would this be useful for you? It's an example of how we test our analytic jacobians numerically in our unit tests. You can follow a similar template to verify the correctness of yours.

We haven't yet added the function in #321 as we have been prioritizing other features, but I'll bump its priority a bit.

Thanks! It looks quite good, I will have a try later

xphsean12 avatar Dec 05 '22 02:12 xphsean12

Would this be useful for you? It's an example of how we test our analytic jacobians numerically in our unit tests. You can follow a similar template to verify the correctness of yours.

We haven't yet added the function in #321 as we have been prioritizing other features, but I'll bump its priority a bit.

Thanks for your support. After reading the code, I have made some modifications and made it simpler for auto-checking by utilizing AutoDiffCostFunction. Details are as belows. I modified it from my use case and skiped some irrelevant part

opt_var1, opt_var2, opt_var3, aux_var1, aux_var2, aux_var3 = prepareInputForCostFunction()
#2. Check Jacobian
cost_weight = th.ScaleCostWeight(1.0)
test_jacobian(opt_var1, opt_var2, opt_var3, aux_var1, aux_var2, aux_var3, cost_weight)

def test_jacobian(opt_var1, opt_var2, opt_var3, aux_var1, aux_var2, aux_var3,  cost_weight):
    cost_function = exampleCost(opt_var1, opt_var2, opt_var3, aux_var1, aux_var2, aux_var3,  cost_weight)

    aux_vars = aux_var1, aux_var2, aux_var3
    optim_vars = opt_var1, opt_var2, opt_var3
    interation = aux_var1.shape[1] # or maybe aux_var1.dof()
   
    cost_function_numeric = th.AutoDiffCostFunction(optim_vars, cost_function.error_numeric, interation, aux_vars=aux_vars)
    
    
    expected_jacs, error_jac = cost_function_numeric.jacobians()
    #print(expected_jacs)
    jacobians, error_jac = cost_function.jacobians()
    error = cost_function.error()
    print("checking Jacobian...")
    assert torch.allclose(error_jac, error)
    for i in range(len(jacobians)):
        assert torch.allclose(jacobians[i], expected_jacs[i], atol=1e-8)



class exampleCost(th.CostFunction):
    def __init__(
        self,
        cost_weight: th.CostWeight,
        opt_var1: th.Vector,
        opt_var2: th.Vector,
        opt_var3: th.Vector,
        aux_var1: th.Vector,
        aux_var2: th.Vector,
        aux_var3: th.Vector,
        cost_weight: CostWeight,
        name: Optional[str] = None,
    ):
        super().__init__(cost_weight, name=name) 
        self.opt_var1 = opt_var1
        self.opt_var2 = opt_var2
        self.opt_var3 = opt_var3
        self.aux_var1 = aux_var1
        self.aux_var2 = aux_var2
        self.aux_var3 = aux_var3
        
        self.register_optim_vars(["opt_var1", "opt_var2", "opt_var3"])
        self.register_aux_vars(["aux_var1", "aux_var2", "aux_var3"])

    def error(self) -> torch.Tensor:
        # self define error 
        error = None
        return error

    def error_numeric(self, opt_var, aux_var) -> torch.Tensor:
        # self define error 
        opt_var1, opt_var2, opt_var3 = opt_var
        aux_var1, aux_var2, aux_var3 = aux_var
        # calculate error, exactly same as error()
        return error

    def jacobians(self) -> Tuple[List[torch.Tensor], torch.Tensor]:
        Jocob_List = []
        return Jocob_List, self.error()

    def dim(self) -> int:
        return self.var.dof()

    def _copy_impl(self, new_name: Optional[str] = None) -> "exampleCost":
       pass

xphsean12 avatar Dec 10 '22 06:12 xphsean12

@xphsean12 Great! Thanks for sharing the changes to make it easier to use for auto checking. Would you be interested in opening a PR with this + some unit tests? It would be nice to add it to the library and make it more accessible for all users.

mhmukadam avatar Jan 02 '23 19:01 mhmukadam

@xphsean12 Great! Thanks for sharing the changes to make it easier to use for auto checking. Would you be interested in opening a PR with this + some unit tests? It would be nice to add it to the library and make it more accessible for all users.

Thanks for your appreciation! I will try to open a PR later when I have time.

xphsean12 avatar Feb 01 '23 06:02 xphsean12

Would this be useful for you? It's an example of how we test our analytic jacobians numerically in our unit tests. You can follow a similar template to verify the correctness of yours.

We haven't yet added the function in #321 as we have been prioritizing other features, but I'll bump its priority a bit.

Here's the updated link for anyone checking today: https://github.com/facebookresearch/theseus/blob/main/tests/theseus_tests/embodied/measurements/test_moving_frame_between.py

It looks like https://github.com/facebookresearch/theseus/issues/321 has been superseded by https://github.com/facebookresearch/theseus/pull/465 which was merged and that the utility is now in https://github.com/facebookresearch/theseus/blob/5db40e9c3572c18f684709eb9758d0b408902bb5/theseus/utils/utils.py#L143-L185 so the issue here might be resolved, to my understanding. Thanks for the efforts!

DanielTakeshi avatar Jun 26 '23 15:06 DanielTakeshi

Hi @DanielTakeshi, thanks for posting the updated info!

For completeness, I think closing the issue would be best after migrating all cost function tests to use the new utility, because a lot of our old unit tests are still based on numerical jacobian references.

luisenp avatar Jun 26 '23 17:06 luisenp