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

Add support for tangent function #231

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

rhannequin
Copy link
Contributor

Associated issue: #81

Context

BigMath module supports sine (sin) and cosine (cos) trigonometric functions. It also supports arctangent (acan). But it doesn't support tangent, which is supported in Math module.

Solution

tan is related to sin and cos function such as (source):

tan(x) = sin(x) / cos(x)

This PR adds BigMath.tan(x, precision), dependent on existing BigMath.sin and BigMath.cos methods.

@rhannequin
Copy link
Contributor Author

Hello @mrkn, anything I should do to this PR to improve it?
Please let me know if I missed a step to have this PR reviewed 🙏
Thank you.

Copy link
Member

@mrkn mrkn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rhannequin Thank you for your cooperation. I left some comments. Could you please check them?

assert_in_delta(0.0, tan(BigDecimal("0.0"), N))
assert_in_delta(0.0, tan(PI(N), N))
assert_in_delta(1.0, tan(PI(N) / 4, N))
assert_in_delta(Math.sqrt(3.0), tan(PI(N) / 3, N))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use BigMath.sqrt(BigDecimal(3), N) instead of Math.sqrt(3.0).
Moreover, I guess we need to use assert_equal instead of assert_in_delta for all the assertions in this test method.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that I think we must replace assert_in_delta with assert_equal in all the other test methods, but they are out of this PR's target.

Copy link
Member

@mrkn mrkn Jul 25, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want to check the 10*N-digit case in addition to the N-digit case for $\pi/3$. So could you add the following assertion?

Suggested change
assert_in_delta(Math.sqrt(3.0), tan(PI(N) / 3, N))
assert_in_delta(Math.sqrt(3.0), tan(PI(N) / 3, N))
assert_equal(sqrt(BigDecimal(3), 10*N), tan(PI(10*N) / 3, 10*N))

Copy link
Contributor Author

@rhannequin rhannequin Jul 28, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello @mrkn, thank you for your review 🙏

I used assert_in_delta for your suggestion because the definition of tan (sin / cos) makes the two methods' precision sum:

require "bigdecimal/math"

BigMath.sin(BigDecimal("1"), 10)
# => 0.84147098480789650665250231788602089836677454e0

BigMath.cos(BigDecimal("1"), 10)
# => 0.540302305868139717400936607637646275593203e0

BigMath.sin(BigDecimal("1"), 10) / BigMath.cos(BigDecimal("1"), 10)
# => 0.1557407724654902230506974799967261008769125258846034219578498192680719427656331763435628519e1

Using assert_equal in this case fails as the result has a lot more precision than N.

Do you think we should change the tan definition to handle precision differently?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think we should change the tan definition to handle precision differently?

We must produce at least N precise digits for BigMath.tan(x, N), so the answer is yes.
Instead, I think we can use assert_in_delta with specifying BigDecimal("1e-#{N+1}") in the delta argument.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello @mrkn
Thank you again for reviewing this PR.

I had a look a other tests on the gem and I found that we use assert_in_delta in most of them, including cos and sin.
I understand that this may not be exactly what we're supposed to use for more accurate testing. Although, do you think we should first add this missing method (I'm also planning on adding acos and asin right after) the way the rest of the gem was implemented?

I'll be happy to help in the same time on updating gem to assert_in_delta in a way that is suitable for BigDecimal.
Please let me know what you think.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason why I'm sticking to a precise alternative of assert_in_delta is that your implementation of tan depends on sin and cos, and they are tested by assert_in_delta.

If tan and other new methods you plan won't depend on other methods tested by assert_in_delta, it can be merged.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for this explanation and the link to the work on test-unit, I see better why it is important to wait for a prior work.
I think it's better to stick to a definition of tan based on sin and cos for the simplicity of the gem (of course I'm all ears to counter arguments).

How can I help on your current PR on test-unit? This is a bit blurry to me but I guess I have to start somewhere.

Copy link
Member

@mrkn mrkn Oct 1, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixing assert_in_delta is almost done. After the new version of test-unit is released, I will update the master branch of this repository. And then, please rebase this branch and fix the test.

I think it's better to stick to a definition of tan based on sin and cos for the simplicity of the gem (of course I'm all ears to counter arguments).

If you can implement tan as a series that converges faster than sin and cos, using the series is better. If not, you can at least depend on only sin by applying basic trigonometric formula $\cos x = 1 - \sin^2 x$. Could you please take and compare benchmark results of both $\sin x / \cos x$ and $\sin x / (1 - \sin^2 x)$ to decide which is better?

Copy link
Member

@mrkn mrkn Oct 1, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additionally, we can treat the case of $x = 0$ as the special case that just returns 0. And, we can limit the sin call only in the case of $x \in (-\pi/2, \pi/2)$ because tan is periodic.

@rhannequin rhannequin requested a review from mrkn July 28, 2022 07:56
@mrkn
Copy link
Member

mrkn commented Sep 25, 2022

I'm working on supporting BigDecimal in assert_in_delta family assertions at test-unit/test-unit#218.

@mrkn
Copy link
Member

mrkn commented Oct 8, 2022

I'm working on adding tests with higher precision at #238

@rhannequin
Copy link
Contributor Author

@mrkn Hello, I'm back after a small break. Maybe I can help with #238?

@mrkn
Copy link
Member

mrkn commented Oct 15, 2022

@rhannequin Although test-unit now supports BigDecimal in assert_in_delta family assertions, as tests of BigDecimal need to run also in Ruby's test-all, I have to let the test framework in Ruby's test-all support the new assert_in_delta family assertions.

Until finishing this work, could you try to rewrite tan implementation to depend on only sin?

@rhannequin
Copy link
Contributor Author

Hello @mrkn, I hope you're doing great.

I did some research for a new algorithm for cos and arrived to the conclusion to just duplicate it, for now, so that tan only depends on sin.

I committed it a few minutes ago, what do you think?

end

sine / cosine
end
Copy link
Member

@mrkn mrkn Oct 31, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rhannequin Could you please mention the origin such as the paper reference of this algorithm in the comment?
And, I want to know how this algorithm is better than just doing sine/(1 - sine**2). Could you explain?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mrkn I don't believe tanθ is defined as sinθ / (1 - sin2θ), but should be sinθ / cosθ and cosθ is √(1 - sin2θ).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nobu The current code was updated against my comment above. Please compare the commit timestamp and the comment timestamp.

@rhannequin
Copy link
Contributor Author

@mrkn Hello, sorry for taking so long to move forward with this pull request.

My math background is not really advanced and I wasn't familiar with this definition of cosine based on sine.
I changed the implementation of tan to only depend on sin with this new definition, which is indeed much clearer and probably much faster.

Would you still like a link to a paper or an "official" definition for the documentation?

@rhannequin
Copy link
Contributor Author

@hsbt I allow myself to ping you here as you seem to have been active on the project recently.
Please let me know if this PR needs to be reviewed by someone in particular, or if anything must be changed in the content.

@mrkn
Copy link
Member

mrkn commented Feb 22, 2024

@rhannequin He’s recent work is only for leaving this library out of Ruby’s default gems. I think there are few people who can review new features of bigdecimal in the Ruby core team. Could you please wait until I can look into your new commits?

@rhannequin
Copy link
Contributor Author

Absolutely, @mrkn. Sorry if my comment sounded pressing, I wasn't sure you had time for this at the moment. I'll wait now all the time needed.

@mrkn
Copy link
Member

mrkn commented Feb 22, 2024

@rhannequin Thank you for your understanding and cooperation!

@mrkn
Copy link
Member

mrkn commented Sep 9, 2024

Since bigdecimal is now a bundled gem, I think we only need to update the version of test-unit to ensure that the tests can verify the full digits.

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

Successfully merging this pull request may close these issues.

3 participants