-
Notifications
You must be signed in to change notification settings - Fork 56
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
Adding batchhafnian code #21
Conversation
…googletest and python test modules. Reformattes with black and astyle.
@bgupt could the following functionality be built into the C++ code? Generating an example: from hafnian import batchhafnian
import numpy as np V = np.array([[ 1.5 , 1.41421356, 0. , 0.],
[ 1.41421356, 1.5 ,0. ,0. ],
[ 0. ,0. ,1.5 , -1.41421356],
[ 0. ,0. ,-1.41421356, 1.5 ]])
rs = np.zeros(2*n) res = 4
indv = res*np.ones(2*n,dtype=int)
A = batchhafnian(V,rs,res) This is the part that would be great to move into the C++ codeIf I understand your code correctly you encode a tensor The function find_rep below allows you to find, given the position of an element in the linear array Note that def find_rep(val, base, n):
x = np.empty([n]) # In C++ this would be a vector of size n
local_val = val
x[0] = 1
for i in range(1,n):
x[i] = x[i-1]*base
digits = np.zeros([n], dtype = int)
for i in range(n):
digits[i] = local_val // x[-i-1] # Careful here this is integer division, not float division, although I think C++ does this by default using /
local_val = local_val - digits[i]*x[-i-1]
return digits find_rep(85,4,4) # the 85th element of the array with 2 modes (n = 2*2) and with local base size base = 4 corrsponds to the 1,1,1,1 element of the tensor
The function below does the following: We know that each element of def renorm(tn, m, res):
facts = np.ones([res])
for i in range(1,res):
facts[i] = i*facts[i-1]
for i in range(1,res):
facts[i] = 1.0/np.sqrt(facts[i])
for i in range(res**m):
digits = find_rep(i, res, m)
pref = 1.0
for j in digits:
pref = pref*facts[j]
tn[i] = tn[i]*pref
return tn B = np.copy(A)
renorm(B, 2*n, res);
For example, the last element of $A$ which corresponds to the 3,3,3,3 element of T is
```python
A[-1]
B[-1]
A[-1]/B[-1] # which is also sqrt(3! 3! 3! 3!)
Would it be possible to make the function batch hafnian in C++ take care of the division by all the factorials, i.e. have it return the array B constructed Above directly? |
One final thing is that it would be great if the inclusion of this factorial parameters could be made optional, i.e., add an extra argument to the C++ function... |
Codecov Report
@@ Coverage Diff @@
## master #21 +/- ##
=========================================
- Coverage 95.21% 95.2% -0.02%
=========================================
Files 8 9 +1
Lines 564 625 +61
=========================================
+ Hits 537 595 +58
- Misses 27 30 +3
Continue to review full report at Codecov.
|
…etest accordingly.
…test when renorm=False. Test for True needs to be written.
src/batchhafnian.hpp
Outdated
@@ -117,6 +136,10 @@ inline std::vector<T> hermite_multidimensional(std::vector<T> &R_mat, std::vecto | |||
std::transform(jumpFrom.begin(), jumpFrom.end(), prevJump.begin(), tmpjump.begin(), std::minus<int>()); | |||
ullint prevCoordinate = vec2index(tmpjump, resolution); | |||
H[nextCoordinate] = H[nextCoordinate] - (static_cast<T>(jumpFrom[ii]-1))*static_cast<T>(R(k,ii))*H[prevCoordinate]; | |||
|
|||
if (renorm) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the renormalization need to occur outside the loop for (ullint jj = 0; jj < Hdim-1; jj++)
otherwise you break the recursion relation. So you need a second loop for (ullint jj = 0; jj < Hdim-1; jj++)
in which do the renormalization after the first loop is completed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Computed the renormalization factor in a separate array in the same loop and used a separate loop to renormalize if renorm = 1
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @bgupt . I left a few comments that should be addressed. In particular because you renomarlize inside the recursion loops you get wrong answers. Also, how hard would it be allow for these calculation to also be done in quad precision?
src/batchhafnian.hpp
Outdated
|
||
if (renorm) { | ||
H[nextCoordinate] = H[nextCoordinate]/renorm_factor(nextPos); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, you should not be calling a function to calculate factorials everytime. Instead just save the square root factorials all the way to resolution
and just multiply those that you need for each element of the tensor. For this you will need to write a function that allows you to find the indices correspoding to a given element of the tensor inside the vector H. Such function in python is:
def find_rep(val, base, n):
x = np.empty([n]) # In C++ this would be a vector of size n
local_val = val
x[0] = 1
for i in range(1,n):
x[i] = x[i-1]*base
digits = np.zeros([n], dtype = int)
for i in range(n):
digits[i] = local_val // x[-i-1] # Careful here this is integer division, not float division, although I think C++ does this by default using /
local_val = local_val - digits[i]*x[-i-1]
return digits
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the suggestion. Implemented that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure if it is a good idea to use integers to represent factorials.
src/batchhafnian.hpp
Outdated
|
||
} | ||
|
||
ullint factorial(int n) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the largest factorial that you can calculate in integer precision ullint
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not too sure about that. But factorial of 21 exceeds the range of unsigned long long int
so I suppose the max would be 20.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But I changed the factorial function from ullint
to long double
. So, it should be better now.
Changed it to |
… Removed gradhaf mentions. Changed factorial data type from unsigned long long int to long double. More efficient computation of the renormalization factors.
hafnian/_hafnian.py
Outdated
raise ValueError("Input matrix must be symmetric.") | ||
if check_symmetry: | ||
if np.linalg.norm(A - A.T) >= tol: | ||
raise ValueError("Input matrix must be symmetric.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How comes this check was added? Is there a use case for attempting to calculate the hafnian of non-symmetric matrices?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is just to make sure that the input matrix is symmetric. It raises error if not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I meant the check_symmetry
flag. Is there a reason to turn off this check?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was because I thought multidimensional hermite polynomials are defined for any matrix. In reality they only make sense for symmetric matrices. Will remove this option
…es in source code.
…for the exceptions
extra_dll_dir = os.path.join(os.path.dirname(__file__), ".libs") | ||
if os.path.isdir(extra_dll_dir): | ||
os.environ["PATH"] += os.pathsep + extra_dll_dir | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isort
probably moved this down, but this will have to be moved back to before the line
from ._hafnian import (
to make sure it imports correctly on windows :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bgupt can you look into this?
r""" | ||
THIS NEEDS TO BE WRITTEN | ||
|
||
Returns the hafnian of matrix with repeated rows/columns. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this a copy/paste mistake 🙂 Probably should undo this change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is. Thanks for catching it up!
C (array): An array | ||
index (array): A set of indices | ||
Returns: | ||
complex: The product of the array elements determines by index |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
complex: The product of the array elements determines by index | |
complex: The product of the array elements determined by index |
def expansion_coeff(alpha, resolution, renorm=True): | ||
""" | ||
Returns the (quasi) geometric series as a vector with components alpha^i/sqrt(i!) for 0 <= i < resolution. | ||
If renorm is false it omits the division by factorials |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can probably delete this line since it's already discussed in the args: list.
If renorm is false it omits the division by factorials |
renorm (bool): If True returns :math:`H_k^{(R)}(y)/\prod(\prod_i k_i!)` | ||
make_tensor: If False returns a flattened one dimensional array instead of a tensor with the values of the polynomial | ||
Returns: | ||
(array): The multidimensional Hermite polynomials. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that you'll need blank lines in three places in order for this to render properly in the docs.
-
After the first line of the docstring. So after "Returns the multidimensional Hermite polynomials :math:
H_k^{(R)}(y)
.", there should be an empty line. -
Before "Args:"
-
Before "Returns"
make_tensor: If False returns a flattened one dimensional array instead of a tensor with the values of the polynomial | ||
Returns: | ||
(array): The values of the hafnians. | ||
""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same comment as above re: line breaks. You can build the docs locally (make doc
) to ensure the docstrings render correctly :)
for i in range(n): | ||
y_mat.push_back(d[i]) | ||
|
||
return hermite_multidimensional_cpp(R_mat, y_mat, resolution, renorm) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a bit confused; if this is the 'batch_hafnian', should we just call the function 'batch_hafnian' instead of hermite_multidimensional?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No. As far as I know this connection has not been made and I think is better to keep them separated. The multidimensional hermite polynomials are a thing that has existed for centuries. The fact that they can be used to calculate batches of hafnians is a new finding...
Co-Authored-By: Josh Izaac <josh146@gmail.com>
Co-Authored-By: Josh Izaac <josh146@gmail.com>
Co-Authored-By: Josh Izaac <josh146@gmail.com>
…urns and also removed the option check_symmetry from input validation
Description of the Change: Code to compute photon number statistics for given Gaussian state with a covariance matrix.
Benefits: Computes probabilities of all photon statistics upto a given cutoff
Possible Drawbacks: None
Related GitHub Issues: None