Skip to content

Commit

Permalink
Merge pull request #5 from fast-reflexes/1.4.2-bugfix-and-improved-ca…
Browse files Browse the repository at this point in the history
…lculation-of-combinations

1.4.2
  • Loading branch information
fast-reflexes authored Mar 10, 2024
2 parents ea67e26 + af70680 commit 80993e5
Show file tree
Hide file tree
Showing 9 changed files with 116 additions and 54 deletions.
3 changes: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,6 @@ target/
profile_default/
ipython_config.py

# pyenv
.python-version

# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
Expand Down
1 change: 1 addition & 0 deletions .python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.10.4
32 changes: 27 additions & 5 deletions BirthdayProblem.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,13 @@ def facultyNTakeMLogE(n, nLogE, m):
nTakeMFacLogE = _DecimalContext.ctx.subtract(nFacLogE, nSubMFacLogE)
return nTakeMFacLogE

@staticmethod
def facultyNaive(n):
nFac = _DecimalFns.ONE
for i in range(int(n),0, -1):
nFac = _DecimalContext.ctx.multiply(nFac, Decimal(i))
return nFac

# faculty method wrapper for both natural and base-2 logarithms
@staticmethod
def facultyLog(n, nLog, isLog2):
Expand Down Expand Up @@ -824,6 +831,12 @@ class _BirthdayProblemInputHandler:
########################################################################################################################################################################################################
########################################################################################################################################################################################################

# threshold for resulting log2 d size input under which we use the exact naive method for calculating inputs with
# both -c and -b flags (for too large inputs we will get overflow when calculating d which is needed for the naive
# method but d is not really needed to solve the problem in log 2 space so then we downgrade to Sterling's
# approximation when processing the inputs instead). The used threshold implies naive calculation of 32768!
LOG2_THRESHOLD_FOR_NAIVE_CALCULATION_OF_D_FOR_COMBINATIONS_AND_BINARY = Decimal('15') # corresponds to 32768

@staticmethod
def illegalInputString(varName = None):
return "Illegal input" if varName is None else "Illegal input for '" + varName + "'"
Expand Down Expand Up @@ -892,11 +905,20 @@ def setup(dOrDLog, nOrNLog, p, isBinary, isCombinations):
if isCombinations:
# d is the size of a set of items, calculate the number of permutations that is possible with it
if isBinary:
dLog = _DecimalFns.facultyLog(_DecimalContext.ctx.power(_DecimalFns.TWO, dOrDLog), dOrDLog, True)
d = _DecimalContext.ctx.power(_DecimalFns.TWO, dLog)
if _DecimalFns.isGreaterThan(dOrDLog, _BirthdayProblemInputHandler.LOG2_THRESHOLD_FOR_NAIVE_CALCULATION_OF_D_FOR_COMBINATIONS_AND_BINARY):
# use approximation
dLog = _DecimalFns.facultyLog(_DecimalContext.ctx.power(_DecimalFns.TWO, dOrDLog), dOrDLog, True)
d = _DecimalContext.ctx.power(_DecimalFns.TWO, dLog)
else:
# use exact calculation
d = _DecimalContext.ctx.power(_DecimalFns.TWO, dOrDLog)
d = _DecimalFns.facultyNaive(d)
dLog = _DecimalContext.ctx.divide(_DecimalContext.ctx.ln(d), _DecimalFns.LOG_E_2)
else:
dLog = _DecimalFns.facultyLog(d, _DecimalContext.ctx.ln(dOrDLog), False)
d = _DecimalContext.ctx.exp(dLog)
# here we always need to display d in the output so if we can't calculate it, the request will fail,
# therefore we can just calculate it in a naive way without log space
d = _DecimalFns.facultyNaive(dOrDLog)
dLog = _DecimalContext.ctx.ln(d)
else:
# d is already the size of the set of combinations
if isBinary:
Expand Down Expand Up @@ -1107,7 +1129,7 @@ def solveJson(d, dLog, n, nLog, p, pPercent, isBinary, isStirling, isTaylor, isE
(n, methodUsed) = _BirthdayProblemSolverChecked.birthdayProblemInv(d, dLog, p, method, isBinary)
lastMethodUsed = methodUsed
except BaseException as e:
methodKey = _BirthdayProblemTextFormatter.methodToText(_BirthdayProblemSolver.CalcPrecision.TAYLOR_APPROX).lower()
methodKey = _BirthdayProblemTextFormatter.methodToText(method).lower()
errorMessage = str(e).lower()
if isinstance(e, KeyboardInterrupt):
res['results'][methodKey] = { 'error': 'interrupted' }
Expand Down
15 changes: 15 additions & 0 deletions DEVELOPERS_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,23 @@

## Good to know about `Decimal` and calculations

* `Decimal`'s are immutable but in some places, an input is wrapped in `Decimal(x)`. This is likely because this input
can sometimes be a regular number OR has been so historically and the creation of a `Decimal` has been left.
* Adjustments of `Decimal`'s via `adjustPrecisions` is an attempt to allow a certain number of decimals to the right of
the comma so that, depending on the integer part, a `Decimal` can have its precision increased or decreased at
different times after some processing has been done. If this results in a too big number, then the precision needed is
too big and we can't carry out the calculations. This limit is set at 1000 digits (out of which 100 at most are to the
right of the comma). Larger numbers than this will result in the calculations failing.
* It takes longer to calculate a loop where a log operation occurs in every iteration rather than a multiplication,
therefore calculating stuff in log space can be more time-consuming but has the advantage of allowing larger numbers
without overflowing.

## How to release

* Create a new branch with the name `X.X.X-feature`
* Commit and push to git
* Merge in git
* Pull master locally
* Add tag with `git tag X.X.X`
* Push the new tag with `git push origin XXX`
* Add new release for tag in Github
43 changes: 27 additions & 16 deletions DataTest.py → JsonTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,29 +79,29 @@
'52 -p 0.1 -c -t',
True,
{
'd': '≈80529020383886612857810199580012764961409004334781435987268084328737 (≈8*10^67)',
'd': '80658175170943878571660636856403766975289505440883277824000000000000 (≈8*10^67)',
'p': '10%',
'results': {
'taylor': {'result': '4119363813276486714957808853108064 (≈4*10^33)'}
'taylor': {'result': '4122665867622533660736208120290868 (≈4*10^33)'}
}
}
],
[
'52 -p 0.5 -c -t',
True,
{
'd': '≈80529020383886612857810199580012764961409004334781435987268084328737 (≈8*10^67)',
'd': '80658175170943878571660636856403766975289505440883277824000000000000 (≈8*10^67)',
'p': '50%',
'results': {
'taylor': {'result': '10565837726592754214318243269428637 (≈10^34)'}
'taylor': {'result': '10574307231100289363611308602026252 (≈10^34)'}
}
}
],
[
'52 -n 10000000000000000000 -c -s -t',
True,
{
'd': '≈80529020383886612857810199580012764961409004334781435987268084328737 (≈8*10^67)',
'd': '80658175170943878571660636856403766975289505440883277824000000000000 (≈8*10^67)',
'n': '10000000000000000000 (=10^19)',
'results': {
'stirling': {'result': '≈0% (≈6*10^-31)'},
Expand All @@ -113,37 +113,37 @@
'52 -n 10000000000000000000000000000000000 -c -s -t',
True,
{
'd': '≈80529020383886612857810199580012764961409004334781435987268084328737 (≈8*10^67)',
'd': '80658175170943878571660636856403766975289505440883277824000000000000 (≈8*10^67)',
'n': '10000000000000000000000000000000000 (=10^34)',
'results': {
'stirling': {'result': '≈46.2536366051%'},
'taylor': {'result': '≈46.2536366051%'}
'stirling': {'result': '≈46.2001746672%'},
'taylor': {'result': '≈46.2001746672%'}
}
}
],
[
'4 -n 18 -b -c -a',
True,
{
'd': '≈2^44.2426274105',
'd': '≈2^44.2501404699',
'n': '2^18',
'results': {
'exact': {'result': '≈0.1649423866% (≈2*10^-3)'},
'stirling': {'result': '≈0.1649422224% (≈2*10^-3)'},
'taylor': {'result': '≈0.1649428504% (≈2*10^-3)'}
'exact': {'result': '≈0.1640861961% (≈2*10^-3)'},
'stirling': {'result': '≈0.1640861961% (≈2*10^-3)'},
'taylor': {'result': '≈0.1640868208% (≈2*10^-3)'}
}
}
],
[
'16 -n 262144 -c -a',
True,
{
'd': '≈20814114415223 (≈2*10^13)',
'd': '20922789888000 (≈2*10^13)',
'n': '262144 (≈3*10^5)',
'results': {
'exact': {'result': '≈0.1649423866% (≈2*10^-3)'},
'stirling': {'result': '≈0.1649422224% (≈2*10^-3)'},
'taylor': {'result': '≈0.1649428504% (≈2*10^-3)'}
'exact': {'result': '≈0.1640861961% (≈2*10^-3)'},
'stirling': {'result': '≈0.1640861961% (≈2*10^-3)'},
'taylor': {'result': '≈0.1640868208% (≈2*10^-3)'}
}
}
],
Expand Down Expand Up @@ -184,6 +184,17 @@
}
}
],
[
'1280 -p 0.5 -b -c -e',
True,
{
'd': '≈2^26614275474014559821953787196100807012412948367028783328633986189111799719299525295290069853854877867120534538070982737886888824825850066183609939356930416666755910887266773840385877776851876084664629106697034459995685244418266399190317043076208186461319737435225525519543453247219560088300601118286958869004726993677805799134087110255288245085785541666888810491274634074724367056992419344.3330052449',
'p': '50%',
'results': {
'exact': {'error': 'd exceeds maximum size and is needed for method'}
}
}
],
[
'12800 -n 6400 -b -c -s -t',
False,
Expand Down
25 changes: 15 additions & 10 deletions LibraryTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,12 +244,12 @@ def testFn(args):
[
{ "dOrDLog": "52", "p": "0.1", "method": BirthdayProblem.Solver.CalcPrecision.TAYLOR_APPROX, "isCombinations": True, "isBinary": False },
True,
(Decimal("4119363813276486714957808853108064"), BirthdayProblem.Solver.CalcPrecision.TAYLOR_APPROX)
(Decimal("4122665867622533660736208120290868"), BirthdayProblem.Solver.CalcPrecision.TAYLOR_APPROX)
],
[
{ "dOrDLog": "52", "p": "0.5", "method": BirthdayProblem.Solver.CalcPrecision.TAYLOR_APPROX, "isCombinations": True, "isBinary": False },
True,
(Decimal("10565837726592754214318243269428637"), BirthdayProblem.Solver.CalcPrecision.TAYLOR_APPROX)
(Decimal("10574307231100289363611308602026252"), BirthdayProblem.Solver.CalcPrecision.TAYLOR_APPROX)
],
[
{ "dOrDLog": "52", "nOrNLog": "10000000000000000000", "method": BirthdayProblem.Solver.CalcPrecision.STIRLING_APPROX, "isCombinations": True, "isBinary": False },
Expand All @@ -264,42 +264,42 @@ def testFn(args):
[
{ "dOrDLog": "52", "nOrNLog": "10000000000000000000000000000000000", "method": BirthdayProblem.Solver.CalcPrecision.STIRLING_APPROX, "isCombinations": True, "isBinary": False },
True,
(Decimal("0.462536366051"), BirthdayProblem.Solver.CalcPrecision.STIRLING_APPROX)
(Decimal("0.462001746672"), BirthdayProblem.Solver.CalcPrecision.STIRLING_APPROX)
],
[
{ "dOrDLog": "52", "nOrNLog": "10000000000000000000000000000000000", "method": BirthdayProblem.Solver.CalcPrecision.TAYLOR_APPROX, "isCombinations": True, "isBinary": False },
True,
(Decimal("0.462536366051"), BirthdayProblem.Solver.CalcPrecision.TAYLOR_APPROX)
(Decimal("0.462001746672"), BirthdayProblem.Solver.CalcPrecision.TAYLOR_APPROX)
],
[
{ "dOrDLog": "4", "nOrNLog": "18", "method": BirthdayProblem.Solver.CalcPrecision.EXACT, "isCombinations": True, "isBinary": True },
True,
(Decimal("0.001649423866"), BirthdayProblem.Solver.CalcPrecision.EXACT)
(Decimal("0.001640861961"), BirthdayProblem.Solver.CalcPrecision.EXACT)
],
[
{ "dOrDLog": "4", "nOrNLog": "18", "method": BirthdayProblem.Solver.CalcPrecision.STIRLING_APPROX, "isCombinations": True, "isBinary": True },
True,
(Decimal("0.001649422224"), BirthdayProblem.Solver.CalcPrecision.STIRLING_APPROX)
(Decimal("0.001640861961"), BirthdayProblem.Solver.CalcPrecision.STIRLING_APPROX)
],
[
{ "dOrDLog": "4", "nOrNLog": "18", "method": BirthdayProblem.Solver.CalcPrecision.TAYLOR_APPROX, "isCombinations": True, "isBinary": True },
True,
(Decimal("0.001649428504"), BirthdayProblem.Solver.CalcPrecision.TAYLOR_APPROX)
(Decimal("0.001640868208"), BirthdayProblem.Solver.CalcPrecision.TAYLOR_APPROX)
],
[
{ "dOrDLog": "16", "nOrNLog": "262144", "method": BirthdayProblem.Solver.CalcPrecision.EXACT, "isCombinations": True, "isBinary": False },
True,
(Decimal("0.001649423866"), BirthdayProblem.Solver.CalcPrecision.EXACT)
(Decimal("0.001640861961"), BirthdayProblem.Solver.CalcPrecision.EXACT)
],
[
{ "dOrDLog": "16", "nOrNLog": "262144", "method": BirthdayProblem.Solver.CalcPrecision.STIRLING_APPROX, "isCombinations": True, "isBinary": False },
True,
(Decimal("0.001649422224"), BirthdayProblem.Solver.CalcPrecision.STIRLING_APPROX)
(Decimal("0.001640861961"), BirthdayProblem.Solver.CalcPrecision.STIRLING_APPROX)
],
[
{ "dOrDLog": "16", "nOrNLog": "262144", "method": BirthdayProblem.Solver.CalcPrecision.TAYLOR_APPROX, "isCombinations": True, "isBinary": False },
True,
(Decimal("0.001649428504"), BirthdayProblem.Solver.CalcPrecision.TAYLOR_APPROX)
(Decimal("0.001640868208"), BirthdayProblem.Solver.CalcPrecision.TAYLOR_APPROX)
],
[
{ "dOrDLog": "20922789888000", "nOrNLog": "262144", "method": BirthdayProblem.Solver.CalcPrecision.EXACT, "isCombinations": False, "isBinary": False },
Expand Down Expand Up @@ -336,6 +336,11 @@ def testFn(args):
True,
(Decimal("0"), BirthdayProblem.Solver.CalcPrecision.TAYLOR_APPROX)
],
[
{ "dOrDLog": "1280", "p": "0.5", "method": BirthdayProblem.Solver.CalcPrecision.EXACT, "isCombinations": True, "isBinary": True },
False,
"d exceeds maximum size and is needed for method"
],
[
{ "dOrDLog": "12800", "nOrNLog": "6400", "method": BirthdayProblem.Solver.CalcPrecision.STIRLING_APPROX, "isCombinations": True, "isBinary": True },
False,
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,3 +161,8 @@ Elias Lousseief (2020)
* Documentation, text and smaller fixes forgotten in v. 1.4.
* Added tests where the project is used as a library (previous tests only used the project's command line API).

* *v. 1.4.2*
* Corrected wrong method flag returned when solving for `N` with overflow in `D`.
* Added exact (naive) calculation of `D` parameter when `-c` flag is used. Earlier this relied on the Sterling
approximation. It still relies on Sterling approximation when `-b` and `-c` are used in combination with an input
`D` larger than 15.
8 changes: 4 additions & 4 deletions RunTests.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import OutputTest
import DataTest
import TextTest
import JsonTest
import LibraryTest
from BirthdayProblem import SolverException
import subprocess
Expand Down Expand Up @@ -65,8 +65,8 @@ def runTest(testData, resFn, assemblerFn, dividerFn):
print("Running all tests...")
print()
tests = [
["OutputTest", OutputTest.testData, OutputTest.testFn, OutputTest.assemblerFn, OutputTest.dividerFn],
["DataTest", DataTest.testData, DataTest.testFn, DataTest.assemblerFn, DataTest.dividerFn],
["TextTest", TextTest.testData, TextTest.testFn, TextTest.assemblerFn, TextTest.dividerFn],
["JsonTest", JsonTest.testData, JsonTest.testFn, JsonTest.assemblerFn, JsonTest.dividerFn],
["LibraryTest", LibraryTest.testData, LibraryTest.testFn, LibraryTest.assemblerFn, LibraryTest.dividerFn]
]
for (testName, testData, testFn, assemblerFn, dividerFn) in tests:
Expand Down
Loading

0 comments on commit 80993e5

Please sign in to comment.