Repository: pyscf/pyscf — pull request #2050 (python)
Title: Improve cistring.addr2str for large address (issue #1886)


Resolved issues:
title: cistring error
body: Using `int32` for the indices causes issues when the range of indices exceeds the representable range of `int32`. For example, the following program crashes with `floating point exception (core dumped)`:
```python
from pyscf.fci import cistring

norb = 48
nelec = 24

string = 0b101100101000111111101011001001010001000100011011
address = cistring.str2addr(norb=norb, nelec=nelec, string=string)
string2 = cistring.addr2str(norb=norb, nelec=nelec, addr=address)
```
Additionally, these functions might succeed but give incorrect results. For example, this code does get to the assert statement, but that assertion fails:
```python
from pyscf.fci import cistring

string = 0b101101111101101111001110111111110111111100
address = cistring.str2addr(norb=63, nelec=32, string=string)
string2 = cistring.addr2str(norb=63, nelec=32, addr=address)
assert string == string2
```


Hints:

Fix patch:
diff --git a/pyscf/fci/cistring.py b/pyscf/fci/cistring.py
index 983178ff54..0ef463d693 100644
--- a/pyscf/fci/cistring.py
+++ b/pyscf/fci/cistring.py
@@ -343,12 +343,13 @@ def addr2str(norb, nelec, addr):
     '''Convert CI determinant address to string'''
     if norb >= 64:
         raise NotImplementedError('norb >= 64')
-    assert num_strings(norb, nelec) > addr
+    max_addr = num_strings(norb, nelec)
+    assert max_addr > addr
 
-    if addr < 2**31:
+    if max_addr < 2**31:
         return addrs2str(norb, nelec, [addr])[0]
-
-    return _addr2str(norb, nelec, addr)
+    else:
+        return _addr2str(norb, nelec, addr)
 
 def _addr2str(norb, nelec, addr):
     if addr == 0 or nelec == norb or nelec == 0:


Test patch:
diff --git a/pyscf/fci/test/test_cistring.py b/pyscf/fci/test/test_cistring.py
index 634d92a7cd..dcd5d6f988 100644
--- a/pyscf/fci/test/test_cistring.py
+++ b/pyscf/fci/test/test_cistring.py
@@ -156,6 +156,12 @@ def test_addr2str(self):
         self.assertEqual(bin(cistring.addr2str(6, 3, 8)), '0b11010')
         self.assertEqual(bin(cistring.addr2str(7, 4, 9)), '0b110011')
 
+        # Test large addresss
+        string = 0b101101111101101111001110111111110111111100
+        address = cistring.str2addr(norb=63, nelec=32, string=string)
+        string2 = cistring.addr2str(norb=63, nelec=32, addr=address)
+        self.assertEqual(string, string2)
+
     def test_str2addr(self):
         self.assertEqual(str2addr(6, 3, int('0b11001' ,2)), cistring.str2addr(6, 3, int('0b11001' ,2)))
         self.assertEqual(str2addr(6, 3, int('0b11010' ,2)), cistring.str2addr(6, 3, int('0b11010' ,2)))