# Nested if statements better formulation

**URL:** <https://discourse.julialang.org/t/nested-if-statements-better-formulation/49715>\
**Category:** New to Julia\
**Tags:** question, performance, optim, optimization\
**Created:** [November 7, 2020, 1:08am UTC](https://discourse.julialang.org/t/nested-if-statements-better-formulation/49715 "2020-11-07T01:08:50Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![optimist](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/optimist/32/14797_2.png) [@optimist](https://discourse.julialang.org/u/optimist)\
**Post date:** [November 7, 2020, 1:08am UTC](https://discourse.julialang.org/t/nested-if-statements-better-formulation/49715/1 "2020-11-07T01:08:50Z")

</div>

Hello, I have written a piece of code that makes use of some conditional statements, but I feel like I am using too many nested `if` statements. Most of these conditional statements are simply changing the bounded interval over which a function is being called (or optimized). I think some these can be replaced by a `while` or `for` loop but I have not been to figure out how to do it in this case.

How can I use a loop here or any better formulation? Would appreciate any help!

```julia
using Optim

function npv(irr,cf,period)
    sum(cf[i]/(1+irr)^period[i] for i in 1:length(cf))
end
function irr(cf)
    if cf[1]==0 && any(x->abs(x)>0,cf[2:end])
        R = Inf
    elseif abs(cf[1])>0 && all(x->x==0,cf[2:end])
        R = NaN
    else
        period = collect(0:length(cf)-1)
        f(x) = npv(x,cf,period)
        result = optimize(x -> f(x)^2,0.0,1.0,Brent();abs_tol=1e-9)
        if Optim.minimum(result) <= 1e-9
            R= Optim.minimizer(result)
        else result = optimize(x -> f(x)^2,1.0,100000000.0,Brent();abs_tol=1e-9)
                if Optim.minimum(result) <= 1e-9
                    R = Optim.minimizer(result)
                    else result = optimize(x -> f(x)^2,-1.0,0.0,Brent();abs_tol=1e-9)
                        if Optim.minimum(result) <= 1e-9
                            R= Optim.minimizer(result)
                        else result = optimize(x -> f(x)^2,-100000000.0,0.0,Brent();abs_tol=1e-9)
                                if Optim.minimum(result) <= 1e-9
                                    R= Optim.minimizer(result)
                                else
                                    R= NaN
                                end
                            end
                        end
                    end
                end
                return R
            end

```

Example usage `irr([-100,10,110])`

---

<div class="post-metadata">

**Author:** ![Oscar\_Smith](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oscar_smith/32/25343_2.png) [@Oscar\_Smith](https://discourse.julialang.org/u/Oscar_Smith)\
**Post date:** [November 7, 2020, 1:16am UTC](https://discourse.julialang.org/t/nested-if-statements-better-formulation/49715/2 "2020-11-07T01:16:57Z")

</div>

One really simple way to clean this up is to remove all of the else statements. That transforms this into

```julia
function irr(cf)
    if cf[1]==0 && any(x->abs(x)>0,cf[2:end])
        return Inf
    if abs(cf[1])>0 && all(x->x==0,cf[2:end])
        return NaN
    end
	period = collect(0:length(cf)-1)
	f(x) = npv(x,cf,period)
	result = optimize(x -> f(x)^2,0.0,1.0,Brent();abs_tol=1e-9)
	if Optim.minimum(result) <= 1e-9
		return Optim.minimizer(result)
	end
	result = optimize(x -> f(x)^2,1.0,100000000.0,Brent();abs_tol=1e-9)
	if Optim.minimum(result) <= 1e-9
		return Optim.minimizer(result)
	end
	result = optimize(x -> f(x)^2,-1.0,0.0,Brent();abs_tol=1e-9)
	if Optim.minimum(result) <= 1e-9
		return Optim.minimizer(result)
	end
	result = optimize(x -> f(x)^2,-100000000.0,0.0,Brent();abs_tol=1e-9)
	if Optim.minimum(result) <= 1e-9
		return Optim.minimizer(result)
	end
	return NaN
end

```

---

<div class="post-metadata">

**Author:** ![optimist](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/optimist/32/14797_2.png) [@optimist](https://discourse.julialang.org/u/optimist)\
**Post date:** [November 7, 2020, 1:23am UTC](https://discourse.julialang.org/t/nested-if-statements-better-formulation/49715/3 "2020-11-07T01:23:19Z")

</div>

Thank you! Do you have any suggestions on how a loop can be used here? I think a loop will give me more flexibility to iterate through an arbitrary number of bounds.

---

<div class="post-metadata">

**Author:** ![optimist](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/optimist/32/14797_2.png) [@optimist](https://discourse.julialang.org/u/optimist)\
**Post date:** [November 7, 2020, 1:35am UTC](https://discourse.julialang.org/t/nested-if-statements-better-formulation/49715/4 "2020-11-07T01:35:49Z")

</div>

Wouldn’t removing else require the function to be evaluated again and again even if the previous condition was satisfied? For eg if the first evaluation of the function satisfies the criteria then it is unnecessary to call the function again to be optimized.

---

<div class="post-metadata">

**Author:** ![Oscar\_Smith](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oscar_smith/32/25343_2.png) [@Oscar\_Smith](https://discourse.julialang.org/u/Oscar_Smith)\
**Post date:** [November 7, 2020, 1:36am UTC](https://discourse.julialang.org/t/nested-if-statements-better-formulation/49715/5 "2020-11-07T01:36:40Z")

</div>

Once you return from a function, you’re out of the function. Since all of your if statements have returns, you never exit out of them.

---

<div class="post-metadata">

**Author:** ![optimist](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/optimist/32/14797_2.png) [@optimist](https://discourse.julialang.org/u/optimist)\
**Post date:** [November 7, 2020, 1:55am UTC](https://discourse.julialang.org/t/nested-if-statements-better-formulation/49715/6 "2020-11-07T01:55:54Z")

</div>

> [@Oscar\_Smith](#):
>
> Since all of your if statements have returns, you never exit out of them.

I have updated the code to get rid of `return` from if statements (see above). Hopefully it should exit the if statements now as soon as the condition is satisfied, but I am novice in Julia so can’t be sure.

---

<div class="post-metadata">

**Author:** ![Oscar\_Smith](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oscar_smith/32/25343_2.png) [@Oscar\_Smith](https://discourse.julialang.org/u/Oscar_Smith)\
**Post date:** [November 7, 2020, 1:58am UTC](https://discourse.julialang.org/t/nested-if-statements-better-formulation/49715/7 "2020-11-07T01:58:03Z")

</div>

Sorry, that was a really confusing statement. What I meant by you never exit out is that you never execute code in the function after the return if you end up in the if statement. You still want the returns in this program to exit out of the function.

---

<div class="post-metadata">

**Author:** ![hendri54](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/hendri54/32/9621_2.png) [@hendri54](https://discourse.julialang.org/u/hendri54)\
**Post date:** [November 7, 2020, 4:06pm UTC](https://discourse.julialang.org/t/nested-if-statements-better-formulation/49715/8 "2020-11-07T16:06:55Z")

</div>

If I understand correctly what you are doing, it’s basically two steps:

1. Find the non-zero values of `cf` that will be actually used in the npv calculations.
2. Run the optimizer on `npv` to find the `irr`. This is done over various predefined intervals until a solution is found.

I would factor out the first part into a function that returns the non-zero indices of `cf` (where checking `abs(x)>0` seems equivalent to `x!=0`).

Then dispatch: if only the first index is non-zero, return NaN. If the first is zero return Inf. What if all are zero? Else do the optimization.

For the optimization, I would set up the bounds as a Vector of Vectors (or similar). Then iterate over the bounds until `optimize` returns something near 0. This way, you don’t need all the `if Optim.minimum(result)` statements and the `optimize` code does not need to be repeated.

---

<div class="post-metadata">

**Author:** ![optimist](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/optimist/32/14797_2.png) [@optimist](https://discourse.julialang.org/u/optimist)\
**Post date:** [November 7, 2020, 7:50pm UTC](https://discourse.julialang.org/t/nested-if-statements-better-formulation/49715/9 "2020-11-07T19:50:20Z")

</div>

> [@hendri54](#):
>
> - Find the non-zero values of `cf` that will be actually used in the npv calculations.
> - Run the optimizer on `npv` to find the `irr` . This is done over various predefined intervals until a solution is found.

In the `irr(cf)` function, the first `if` and `elseif` statements are intended to exclude the cases where there will be no solution. Specifically, if the first element in `cf` is zero and there are any other non-zero elements, then it should return `Inf`. If the first element in `cf` is non-zero but the remainder are zeros then it should return `NaN`.

Now, if these two conditional statements are not true then the optimizer is run on `npv` to find `irr`. The `npv` will use all elements of `cf` including zeros (of course excluding the cases that would fall in the first two conditional statements), e.g.

```julia
julia> irr([-100,0,10,10,0,0,110])
0.049594202242656926

```

> [@hendri54](#):
>
> What if all are zero?

I should have added an `if` statement to return `NaN` or a numerical error in this case.

The optimizer may fail to converge if the bounds are set too wide or give a negative result when a positive result is possible. This is the reason I am changing the bounds.

> [@hendri54](#):
>
> For the optimization, I would set up the bounds as a Vector of Vectors (or similar). Then iterate over the bounds until `optimize` returns something near 0.

This is the bit I am struggling with. I can define the bounds as below, but I do not understand how to iterate over them until the convergence criteria is satisfied, probably a `while` loop is required. How should I use the loop?

```julia
lb= [0.0,1.0,-1.0,-1.0e8]  
up=[1.0,1.0e8,0.0,0.0] 
bounds = hcat(lb,up)

```

---

<div class="post-metadata">

**Author:** ![hendri54](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/hendri54/32/9621_2.png) [@hendri54](https://discourse.julialang.org/u/hendri54)\
**Post date:** [November 7, 2020, 10:49pm UTC](https://discourse.julialang.org/t/nested-if-statements-better-formulation/49715/10 "2020-11-07T22:49:35Z")

</div>

I see - zeros are valid values.

As for the bounds:

```julia
lb= [0.0,1.0,-1.0,-1.0e8]  
up=[1.0,1.0e8,0.0,0.0] 
done = false;
j = 1;
while !done
  result = optimize(x -> f(x)^2, lb[j], ub[j], Brent());
  if Optim.minimum(result) <= 1e-9
    done = true
  elseif j == length(lb)
    # handle that error
  else
    j += 1;
  end
end

```

---

<div class="post-metadata">

**Author:** ![optimist](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/optimist/32/14797_2.png) [@optimist](https://discourse.julialang.org/u/optimist)\
**Post date:** [November 8, 2020, 1:33am UTC](https://discourse.julialang.org/t/nested-if-statements-better-formulation/49715/11 "2020-11-08T01:33:24Z")

</div>

Thank you. With your code, the function does not handle the case where all elements of `cf` are positive or all elements negative, e.g `irr([100,100,101,10])` hangs the program, whereas my original code above would return `NaN`. I have given my full code based on your suggestion below.

> [@hendri54](#):
>
> ```julia
> elseif j == length(lb)
> # handle that error
> 
> ```

I do not understand this part. Do we need a break statement here?

```julia
using Optim

function npv(irr,cf,period)
    sum(cf[i]/(1+irr)^period[i] for i in 1:length(cf))
end

function irr(cf)
   if cf[1]==0 && any(x->x!=0,cf[2:end])
		return Inf
	elseif abs(cf[1])>0 && all(x->x==0,cf[2:end])
		return NaN
	elseif all(x->x==0,cf)
		return NaN
	else
		
	period = collect(0:length(cf)-1)
	f(x) = npv(x,cf,period)
	lb= [0.0,1.0,-1.0,-1.0e8]
	ub=[1.0,1.0e8,0.0,0.0]
	done = false;
	j = 1;
	while !done
		result = optimize(x -> f(x)^2, lb[j], ub[j], Brent());
		if Optim.minimum(result) <= 1e-9
			done = true
			return Optim.minimizer(result)
			elseif j == length(lb)
			# handle that error
			else
				j += 1;
			end
		end
	end
end	

```

---

<div class="post-metadata">

**Author:** ![hendri54](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/hendri54/32/9621_2.png) [@hendri54](https://discourse.julialang.org/u/hendri54)\
**Post date:** [November 8, 2020, 3:15pm UTC](https://discourse.julialang.org/t/nested-if-statements-better-formulation/49715/12 "2020-11-08T15:15:22Z")

</div>

I was just sketching this. The following looks complete to me:

```julia
using Optim

# This is, btw not efficient given that you only use consecutive periods.
# Instead of the `^period[i]` it would be more efficient to loop as in
# pv = 0.0
# R = 1 + irr
# cumR = R
# for j = 1 : length(cf)
# cumR /= R
# pv += cf[j] / cumR
# end
function npv(irr,cf,period)
    sum(cf[i]/(1+irr)^period[i] for i in 1:length(cf))
end

function irr(cf)
	# Get non-zero indices
	idx = findall(x -> x != 0.0, cf);
	# Make sure solution may exist
	if length(idx) < 2
		return NaN
	elseif !(any(x -> x > 0.0, cf) && any(x -> x < 0.0, cf))
		return NaN
	end

	lb= [0.0,1.0,-1.0,-1.0e8]  
	up=[1.0,1.0e8,0.0,0.0] 
	done = false;
	j = 1;
	result = 0.0;
	f(x) = npv(x, cf[idx], 0 : length(idx) - 1)
	while !done
	  result = optimize(x -> f(x)^2, lb[j], up[j], Brent());
	  if Optim.minimum(result) <= 1e-9
		done = true
	  elseif j == length(lb)
	    # No more bounds to try. Solution does not exist.
		done = true
		result = NaN
	  else
	    # Next set of bounds to try
		j += 1;
	  end
	end
	return result
end

irr([-100,10,110])

```

Instead of checking whether the first entry is non-zero etc, I am simply checking whether there are any positive and negative entries.

---

<div class="post-metadata">

**Author:** ![optimist](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/optimist/32/14797_2.png) [@optimist](https://discourse.julialang.org/u/optimist)\
**Post date:** [November 8, 2020, 6:51pm UTC](https://discourse.julialang.org/t/nested-if-statements-better-formulation/49715/13 "2020-11-08T18:51:09Z")

</div>

Thanks you!

> [@hendri54](#):
>
> `return result`

I will change this line of code to `return Optim.minimizer(result)`

> [@hendri54](#):
>
> ```julia
> # This is, btw not efficient given that you only use consecutive periods.
> # Instead of the `^period[i]` it would be more efficient to loop as in
> 
> ```

Using a loop makes the `npv` function slightly faster. The interesting thing that I observed was if I use `@floop` macro before the for loop it makes it a bit slower.

```julia
function npv_loop(irr,cf,period)
	pv = 0.0
	R = 1 + irr
	cumR = R
	for j = 1 : length(cf)
	   cumR /= R
	   pv += cf[j] / cumR
	end
	return pv
end

```

```julia
using FLoops
function npv_Floop(irr,cf,period)
	@floop begin
		pv = 0.0
		R = 1 + irr
		cumR = R
		for j = 1 : length(cf)
			cumR /= R
			pv += cf[j] / cumR
		end
	end
	return pv
end

```

```julia
@btime npv_loop(0.01,[10,10,10],[1,2,3])
  72.661 ns (2 allocations: 224 bytes)
30.301000000000002

```

```julia
@btime npv_Floop(0.01,[10,10,10],[1,2,3])
  76.700 ns (2 allocations: 224 bytes)
30.301000000000002

```
