Skip to content

Commit 732a817

Browse files
authored
Merge pull request #364 from tequilahub/update-to-1.9.7
Update to 1.9.7
2 parents 17e4030 + dc0e99b commit 732a817

File tree

7 files changed

+70
-49
lines changed

7 files changed

+70
-49
lines changed

Diff for: requirements.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# necessary
22
numpy
3-
scipy < 1.11 # for now, until issues with scipy/pyscf are fixed # can in principle be smaller, we recommend >= 1.5 for consistency with our tutorials (numerical gradients mostly)
3+
scipy
44
sympy
55
#jax
66
#jaxlib

Diff for: src/tequila/circuit/circuit.py

+2-5
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,7 @@ def export_to(self, *args, **kwargs):
4646
Convenience: see src/tequila/circuit/qpic.py - export_to for more
4747
Parameters
4848
"""
49-
# this way we allow calling U.export_to("asd.png") instead of having to specify U.export_to(filename="asd.png")
50-
if "circuit" not in kwargs:
51-
kwargs["circuit"]=self
52-
return export_to(*args, **kwargs)
49+
return export_to(self, *args, **kwargs)
5350

5451
@property
5552
def moments(self):
@@ -389,7 +386,7 @@ def __iadd__(self, other):
389386
for k, v in other._parameter_map.items():
390387
self._parameter_map[k] += [(x[0] + offset, x[1]) for x in v]
391388

392-
self._gates += other.gates
389+
self._gates += copy.deepcopy(other.gates)
393390
self._min_n_qubits = max(self._min_n_qubits, other._min_n_qubits)
394391

395392
return self

Diff for: src/tequila/quantumchemistry/qc_base.py

+32-19
Original file line numberDiff line numberDiff line change
@@ -1737,7 +1737,7 @@ def rdm2(self):
17371737

17381738
def compute_rdms(self, U: QCircuit = None, variables: Variables = None, spin_free: bool = True,
17391739
get_rdm1: bool = True, get_rdm2: bool = True, ordering="dirac", use_hcb: bool = False,
1740-
rdm_trafo: QubitHamiltonian = None, decompose=None):
1740+
rdm_trafo: QubitHamiltonian = None, evaluate=True):
17411741
"""
17421742
Computes the one- and two-particle reduced density matrices (rdm1 and rdm2) given
17431743
a unitary U. This method uses the standard ordering in physics as denoted below.
@@ -1768,7 +1768,10 @@ def compute_rdms(self, U: QCircuit = None, variables: Variables = None, spin_fre
17681768
rdm_trafo :
17691769
The rdm operators can be transformed, e.g., a^dagger_i a_j -> U^dagger a^dagger_i a_j U,
17701770
where U represents the transformation. The default is set to None, implying that U equas the identity.
1771-
1771+
evaluate :
1772+
if true, the tequila expectation values are evaluated directly via the tq.simulate command.
1773+
the protocol is optimized to avoid repetation of wavefunction simulation
1774+
if false, the rdms are returned as tq.QTensors
17721775
Returns
17731776
-------
17741777
"""
@@ -1891,13 +1894,14 @@ def _build_2bdy_operators_spinfree() -> list:
18911894
ops += [op]
18921895
return ops
18931896

1894-
def _assemble_rdm1(evals) -> numpy.ndarray:
1897+
def _assemble_rdm1(evals, rdm1=None) -> numpy.ndarray:
18951898
"""
18961899
Returns spin-ful or spin-free one-particle RDM built by symmetry conditions
18971900
Same symmetry with or without spin, so we can use the same function
18981901
"""
18991902
N = n_MOs if spin_free else n_SOs
1900-
rdm1 = numpy.zeros([N, N])
1903+
if rdm1 is None:
1904+
rdm1 = numpy.zeros([N, N])
19011905
ctr: int = 0
19021906
for p in range(N):
19031907
for q in range(p + 1):
@@ -1908,10 +1912,11 @@ def _assemble_rdm1(evals) -> numpy.ndarray:
19081912

19091913
return rdm1
19101914

1911-
def _assemble_rdm2_spinful(evals) -> numpy.ndarray:
1915+
def _assemble_rdm2_spinful(evals, rdm2=None) -> numpy.ndarray:
19121916
""" Returns spin-ful two-particle RDM built by symmetry conditions """
19131917
ctr: int = 0
1914-
rdm2 = numpy.zeros([n_SOs, n_SOs, n_SOs, n_SOs])
1918+
if rdm2 is None:
1919+
rdm2 = numpy.zeros([n_SOs, n_SOs, n_SOs, n_SOs])
19151920
for p in range(n_SOs):
19161921
for q in range(p):
19171922
for r in range(n_SOs):
@@ -1933,10 +1938,11 @@ def _assemble_rdm2_spinful(evals) -> numpy.ndarray:
19331938

19341939
return rdm2
19351940

1936-
def _assemble_rdm2_spinfree(evals) -> numpy.ndarray:
1941+
def _assemble_rdm2_spinfree(evals, rdm2=None) -> numpy.ndarray:
19371942
""" Returns spin-free two-particle RDM built by symmetry conditions """
19381943
ctr: int = 0
1939-
rdm2 = numpy.zeros([n_MOs, n_MOs, n_MOs, n_MOs])
1944+
if rdm2 is None:
1945+
rdm2 = numpy.zeros([n_MOs, n_MOs, n_MOs, n_MOs])
19401946
for p, q, r, s in product(range(n_MOs), repeat=4):
19411947
if p * n_MOs + q >= r * n_MOs + s and (p >= q or r >= s):
19421948
rdm2[p, q, r, s] = evals[ctr]
@@ -2012,18 +2018,25 @@ def _build_2bdy_operators_hcb() -> list:
20122018
# Transform operator lists to QubitHamiltonians
20132019
if (not use_hcb):
20142020
qops = [_get_qop_hermitian(op) for op in qops]
2015-
2021+
20162022
# Compute expected values
2017-
if rdm_trafo is None:
2018-
if decompose is not None:
2019-
print("MANIPULATED")
2020-
X = decompose(H=qops, U=U)
2021-
evals = simulate(X, variables=variables)
2023+
rdm1 = None
2024+
rdm2 = None
2025+
from tequila import QTensor
2026+
if evaluate:
2027+
if rdm_trafo is None:
2028+
evals = simulate(ExpectationValue(H=qops, U=U, shape=[len(qops)]), variables=variables)
20222029
else:
2030+
qops = [rdm_trafo.dagger()*qops[i]*rdm_trafo for i in range(len(qops))]
20232031
evals = simulate(ExpectationValue(H=qops, U=U, shape=[len(qops)]), variables=variables)
20242032
else:
2025-
qops = [rdm_trafo.dagger()*qops[i]*rdm_trafo for i in range(len(qops))]
2026-
evals = simulate(ExpectationValue(H=qops, U=U, shape=[len(qops)]), variables=variables)
2033+
if rdm_trafo is None:
2034+
evals = [ExpectationValue(H=x, U=U) for x in qops]
2035+
N = n_MOs if spin_free else n_SOs
2036+
rdm1 = QTensor(shape=[N,N])
2037+
rdm2 = QTensor(shape=[N, N, N, N])
2038+
else:
2039+
raise TequilaException("compute_rdms: rdm_trafo was set but evaluate flag is False (not supported)")
20272040

20282041
# Assemble density matrices
20292042
# If self._rdm1, self._rdm2 exist, reset them if they are of the other spin-type
@@ -2044,11 +2057,11 @@ def _reset_rdm(rdm):
20442057
len_1 = 0
20452058
evals_1, evals_2 = evals[:len_1], evals[len_1:]
20462059
# Build matrices using the expectation values
2047-
self._rdm1 = _assemble_rdm1(evals_1) if get_rdm1 else self._rdm1
2060+
self._rdm1 = _assemble_rdm1(evals_1, rdm1=rdm1) if get_rdm1 else self._rdm1
20482061
if spin_free or use_hcb:
2049-
self._rdm2 = _assemble_rdm2_spinfree(evals_2) if get_rdm2 else self._rdm2
2062+
self._rdm2 = _assemble_rdm2_spinfree(evals_2, rdm2=rdm2) if get_rdm2 else self._rdm2
20502063
else:
2051-
self._rdm2 = _assemble_rdm2_spinful(evals_2) if get_rdm2 else self._rdm2
2064+
self._rdm2 = _assemble_rdm2_spinful(evals_2, rdm2=rdm2) if get_rdm2 else self._rdm2
20522065

20532066
if get_rdm2:
20542067
rdm2 = NBodyTensor(elems=self.rdm2, ordering="dirac", verify=False)

Diff for: src/tequila/simulators/simulator_base.py

+13-5
Original file line numberDiff line numberDiff line change
@@ -356,15 +356,23 @@ def simulate(self, variables, initial_state=0, *args, **kwargs) -> QubitWaveFunc
356356
raise TequilaException("only product states as initial states accepted")
357357
initial_state = list(initial_state.keys())[0].integer
358358

359-
all_qubits = [i for i in range(self.abstract_circuit.n_qubits)]
359+
all_qubits = list(range(self.abstract_circuit.n_qubits))
360360
active_qubits = self.qubit_map.keys()
361361

362-
# maps from reduced register to full register
363-
keymap = KeyMapSubregisterToRegister(subregister=active_qubits, register=all_qubits)
362+
# Keymap is only necessary if not all qubits are active
363+
keymap_required = sorted(active_qubits) != all_qubits
364364

365-
result = self.do_simulate(variables=variables, initial_state=keymap.inverted(initial_state).integer, *args,
365+
if keymap_required:
366+
# maps from reduced register to full register
367+
keymap = KeyMapSubregisterToRegister(subregister=active_qubits, register=all_qubits)
368+
369+
mapped_initial_state = keymap.inverted(initial_state).integer if keymap_required else int(initial_state)
370+
result = self.do_simulate(variables=variables, initial_state=mapped_initial_state, *args,
366371
**kwargs)
367-
result.apply_keymap(keymap=keymap, initial_state=initial_state)
372+
373+
if keymap_required:
374+
result.apply_keymap(keymap=keymap, initial_state=initial_state)
375+
368376
return result
369377

370378
def sample(self, variables, samples, read_out_qubits=None, circuit=None, *args, **kwargs):

Diff for: src/tequila/utils/bitstrings.py

+19-10
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from enum import Enum
22
from typing import List
33
from functools import total_ordering
4+
from math import ceil, log2
45

56

67
class BitNumbering(Enum):
@@ -35,7 +36,7 @@ def nbits(self, value):
3536

3637
def update_nbits(self):
3738
current = self.nbits
38-
min_needed = len(format(self._value, 'b'))
39+
min_needed = ceil(log2(self._value + 1))
3940
self._nbits = max(current, min_needed)
4041
return self
4142

@@ -177,15 +178,23 @@ def numbering(self) -> BitNumbering:
177178
return BitNumbering.LSB
178179

179180

181+
def _reverse_int_bits(x: int, nbits: int) -> int:
182+
if nbits is None:
183+
nbits = x.bit_length()
184+
assert nbits <= 32
185+
186+
x = ((x & 0x55555555) << 1) | ((x & 0xAAAAAAAA) >> 1)
187+
x = ((x & 0x33333333) << 2) | ((x & 0xCCCCCCCC) >> 2)
188+
x = ((x & 0x0F0F0F0F) << 4) | ((x & 0xF0F0F0F0) >> 4)
189+
x = ((x & 0x00FF00FF) << 8) | ((x & 0xFF00FF00) >> 8)
190+
x = ((x & 0x0000FFFF) << 16) | ((x & 0xFFFF0000) >> 16)
191+
return x >> (32 - nbits)
192+
193+
180194
def initialize_bitstring(integer: int, nbits: int = None, numbering_in: BitNumbering = BitNumbering.MSB,
181195
numbering_out: BitNumbering = BitNumbering.MSB):
182-
if numbering_in == BitNumbering.MSB:
183-
if numbering_out == BitNumbering.MSB:
184-
return BitString.from_int(integer=integer, nbits=nbits)
185-
else:
186-
return BitString.from_binary(binary=BitStringLSB.from_int(integer=integer, nbits=nbits).binary, nbits=nbits)
196+
integer = _reverse_int_bits(integer, nbits) if numbering_in != numbering_out else integer
197+
if numbering_out == BitNumbering.MSB:
198+
return BitString.from_int(integer=integer, nbits=nbits)
187199
else:
188-
if numbering_out == BitNumbering.LSB:
189-
return BitStringLSB.from_int(integer=integer, nbits=nbits)
190-
else:
191-
return BitStringLSB.from_binary(binary=BitString.from_int(integer=integer, nbits=nbits).binary, nbits=nbits)
200+
return BitStringLSB.from_int(integer=integer, nbits=nbits)

Diff for: src/tequila/version.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
__version__ = "1.9.6"
1+
__version__ = "1.9.7"
22
__author__ = "Tequila Developers "

Diff for: src/tequila/wavefunction/qubit_wavefunction.py

+2-8
Original file line numberDiff line numberDiff line change
@@ -145,18 +145,12 @@ def from_array(cls, arr: numpy.ndarray, keymap=None, threshold: float = 1.e-6,
145145
maxkey = len(arr) - 1
146146
maxbit = initialize_bitstring(integer=maxkey, numbering_in=numbering, numbering_out=cls.numbering).nbits
147147
for ii, v in enumerate(arr):
148-
i = initialize_bitstring(integer=ii, nbits=maxbit, numbering_in=numbering, numbering_out=cls.numbering)
149-
if not numpy.isclose(abs(v), 0.0, atol=threshold):
148+
if abs(v) > threshold:
149+
i = initialize_bitstring(integer=ii, nbits=maxbit, numbering_in=numbering, numbering_out=cls.numbering)
150150
key = i if keymap is None else keymap(i)
151151
state[key] = v
152152
result = QubitWaveFunction(state, n_qubits=n_qubits)
153153

154-
if cls.numbering != numbering:
155-
if cls.numbering == BitNumbering.MSB:
156-
result.apply_keymap(keymap=KeyMapLSB2MSB())
157-
else:
158-
result.apply_keymap(keymap=KeyMapMSB2LSB())
159-
160154
return result
161155

162156
@classmethod

0 commit comments

Comments
 (0)