Skip to content

Commit

Permalink
Refactor QubitWaveFunction, add support for Numpy array backed dense …
Browse files Browse the repository at this point in the history
…representation
  • Loading branch information
ohuettenhofer committed Nov 11, 2024
1 parent 5d6e8ae commit ef3587d
Show file tree
Hide file tree
Showing 22 changed files with 444 additions and 361 deletions.
3 changes: 1 addition & 2 deletions src/tequila/apps/unary_state_prep.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ def __init__(self, target_space: typing.List[BitString], max_repeat: int = 100,
simulator.convert_to_numpy = False
variables = None # {k:k.name.evalf() for k in self._abstract_circuit.extract_variables()}
wfn = simulator.simulate(initial_state=BitString.from_int(0, nbits=self.n_qubits), variables=variables)
wfn.n_qubits = self._n_qubits
equations = []
for k in target_space:
equations.append(wfn[k] - abstract_coefficients[k])
Expand Down Expand Up @@ -174,7 +173,7 @@ def __call__(self, wfn: QubitWaveFunction) -> QCircuit:
:return:
"""
try:
assert (len(wfn) == len(self._target_space))
assert wfn.length() == len(self._target_space)
for key in wfn.keys():
try:
assert (key in self._target_space)
Expand Down
7 changes: 4 additions & 3 deletions src/tequila/hamiltonian/paulis.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ def Projector(wfn, threshold=0.0, n_qubits=None) -> QubitHamiltonian:
"""

wfn = QubitWaveFunction(state=wfn, n_qubits=n_qubits)
wfn = QubitWaveFunction.convert_from(n_qubits, wfn)

H = QubitHamiltonian.zero()
for k1, v1 in wfn.items():
Expand Down Expand Up @@ -304,8 +304,9 @@ def KetBra(ket: QubitWaveFunction, bra: QubitWaveFunction, hermitian: bool = Fal
"""
H = QubitHamiltonian.zero()
ket = QubitWaveFunction(state=ket, n_qubits=n_qubits)
bra = QubitWaveFunction(state=bra, n_qubits=n_qubits)

ket = QubitWaveFunction.convert_from(n_qubits, ket)
bra = QubitWaveFunction.convert_from(n_qubits, bra)

for k1, v1 in bra.items():
for k2, v2 in ket.items():
Expand Down
2 changes: 1 addition & 1 deletion src/tequila/quantumchemistry/encodings.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ def map_state(self, state: list, *args, **kwargs) -> list:
fop = openfermion.FermionOperator(string, 1.0)
op = self(fop)
from tequila.wavefunction.qubit_wavefunction import QubitWaveFunction
wfn = QubitWaveFunction.from_int(0, n_qubits=n_qubits)
wfn = QubitWaveFunction.from_basis_state(0, n_qubits=n_qubits)
wfn = wfn.apply_qubitoperator(operator=op)
assert (len(wfn.keys()) == 1)
key = list(wfn.keys())[0].array
Expand Down
4 changes: 2 additions & 2 deletions src/tequila/simulators/simulator_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,7 @@ def compile_circuit(abstract_circuit: 'QCircuit',
return CircType(abstract_circuit=abstract_circuit, variables=variables, noise=noise, device=device, *args, **kwargs)


def simulate(objective: typing.Union['Objective', 'QCircuit','QTensor'],
def simulate(objective: typing.Union['Objective', 'QCircuit', 'QTensor'],
variables: Dict[Union[Variable, Hashable], RealNumber] = None,
samples: int = None,
backend: str = None,
Expand Down Expand Up @@ -407,7 +407,7 @@ def simulate(objective: typing.Union['Objective', 'QCircuit','QTensor'],
objective.extract_variables()))

compiled_objective = compile(objective=objective, samples=samples, variables=variables, backend=backend,
noise=noise,device=device, *args, **kwargs)
noise=noise, device=device, *args, **kwargs)

return compiled_objective(variables=variables, samples=samples, *args, **kwargs)

Expand Down
2 changes: 1 addition & 1 deletion src/tequila/simulators/simulator_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ def simulate(self, variables, initial_state=0, *args, **kwargs) -> QubitWaveFunc
**kwargs)

if keymap_required:
result.apply_keymap(keymap=keymap, initial_state=initial_state)
result = QubitWaveFunction.from_wavefunction(result, keymap, n_qubits=len(all_qubits), initial_state=initial_state)

return result

Expand Down
12 changes: 6 additions & 6 deletions src/tequila/simulators/simulator_cirq.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ def do_simulate(self, variables, initial_state=0, *args, **kwargs) -> QubitWaveF
simulator = cirq.Simulator()
backend_result = simulator.simulate(program=self.circuit, param_resolver=self.resolver,
initial_state=initial_state)
return QubitWaveFunction.from_array(arr=backend_result.final_state_vector, numbering=self.numbering)
return QubitWaveFunction.from_array(array=backend_result.final_state_vector, numbering=self.numbering)

def convert_measurements(self, backend_result: cirq.Result) -> QubitWaveFunction:
"""
Expand All @@ -186,18 +186,18 @@ def convert_measurements(self, backend_result: cirq.Result) -> QubitWaveFunction
Returns
-------
QubitWaveFunction:
the result of sampling, as a tequila QubitWavefunction.
the result of sampling, as a tequila QubitWaveFunction.
"""
assert (len(backend_result.measurements) == 1)
for key, value in backend_result.measurements.items():
counter = QubitWaveFunction()
counter = QubitWaveFunction(self.n_qubits, self.numbering)
for sample in value:
binary = BitString.from_array(array=sample.astype(int))
if binary in counter._state:
counter._state[binary] += 1
if binary in counter.keys():
counter[binary] += 1
else:
counter._state[binary] = 1
counter[binary] = 1
return counter

def do_sample(self, samples, circuit, *args, **kwargs) -> QubitWaveFunction:
Expand Down
6 changes: 3 additions & 3 deletions src/tequila/simulators/simulator_pyquil.py
Original file line number Diff line number Diff line change
Expand Up @@ -439,7 +439,7 @@ def do_simulate(self, variables, initial_state, *args, **kwargs):
if val > 0:
iprep += pyquil.gates.X(i)
backend_result = simulator.wavefunction(iprep + self.circuit, memory_map=self.resolver)
return QubitWaveFunction.from_array(arr=backend_result.amplitudes, numbering=self.numbering)
return QubitWaveFunction.from_array(array=backend_result.amplitudes, numbering=self.numbering)

def do_sample(self, samples, circuit, *args, **kwargs) -> QubitWaveFunction:
"""
Expand Down Expand Up @@ -495,7 +495,7 @@ def string_to_array(s):
listing.append(int(letter))
return listing

result = QubitWaveFunction()
result = QubitWaveFunction(self.n_qubits, self.numbering)
bit_dict = {}
for b in backend_result:
try:
Expand All @@ -505,7 +505,7 @@ def string_to_array(s):

for k, v in bit_dict.items():
arr = string_to_array(k)
result._state[BitString.from_array(arr)] = v
result[BitString.from_array(arr)] = v
return result

def no_translation(self, abstract_circuit):
Expand Down
30 changes: 14 additions & 16 deletions src/tequila/simulators/simulator_qibo.py
Original file line number Diff line number Diff line change
Expand Up @@ -356,20 +356,20 @@ def do_simulate(self, variables, initial_state=None, *args, **kwargs):
n_qubits = max(self.highest_qubit + 1, self.n_qubits, self.abstract_circuit.max_qubit() + 1)
if initial_state is not None:
if isinstance(initial_state, (int, np.int64)):
wave = QubitWaveFunction.from_int(i=initial_state, n_qubits=n_qubits)
wave = QubitWaveFunction.from_basis_state(n_qubits, initial_state, self.numbering)
elif isinstance(initial_state, str):
wave = QubitWaveFunction.from_string(string=initial_state).to_array()
wave = QubitWaveFunction.from_string(initial_state, self.numbering).to_array()
elif isinstance(initial_state, QubitWaveFunction):
wave = initial_state
elif isinstance(initial_state,np.ndarray):
wave = QubitWaveFunction.from_array(initial_state)
wave = QubitWaveFunction.from_array(initial_state, self.numbering)
else:
raise TequilaQiboException('could not understand initial state of type {}'.format(type(initial_state)))
state = wave.to_array()
result = self.circuit(state)
else:
result = self.circuit()
back= QubitWaveFunction.from_array(arr=result.numpy())
back= QubitWaveFunction.from_array(result.numpy(), self.numbering)
return back

def simulate(self, variables, initial_state=0, *args, **kwargs) -> QubitWaveFunction:
Expand Down Expand Up @@ -398,7 +398,7 @@ def simulate(self, variables, initial_state=0, *args, **kwargs) -> QubitWaveFunc
if isinstance(initial_state, BitString):
initial_state = initial_state.integer
if isinstance(initial_state, QubitWaveFunction):
if len(initial_state.keys()) != 1:
if len(initial_state) != 1:
return self.do_simulate(variables=variables,initial_state=initial_state, *args, **kwargs)
initial_state = list(initial_state.keys())[0].integer
if isinstance(initial_state,np.ndarray):
Expand Down Expand Up @@ -426,19 +426,18 @@ def convert_measurements(self, backend_result, target_qubits=None) -> QubitWaveF
results transformed to tequila native QubitWaveFunction
"""

result = QubitWaveFunction()
result = QubitWaveFunction(self.n_qubits, self.numbering)
# todo there are faster ways

for k, v in backend_result.frequencies(binary=True).items():
converted_key = BitString.from_bitstring(other=BitString.from_binary(binary=k))
result._state[converted_key] = v

result[converted_key] = v

if target_qubits is not None:
mapped_target = [self.qubit_map[q].number for q in target_qubits]
mapped_full = [self.qubit_map[q].number for q in self.abstract_qubits]
keymap = KeyMapRegisterToSubregister(subregister=mapped_target, register=mapped_full)
result = result.apply_keymap(keymap=keymap)
result = QubitWaveFunction.from_wavefunction(result, keymap, len(mapped_target))

return result

Expand Down Expand Up @@ -521,21 +520,20 @@ def do_sample(self, samples, circuit, noise_model=None, initial_state=None, *arg
n_qubits = max(self.highest_qubit + 1, self.n_qubits, self.abstract_circuit.max_qubit() + 1)
if initial_state is not None:
if isinstance(initial_state, int):
wave=QubitWaveFunction.from_int(i=initial_state, n_qubits=n_qubits)
wave = QubitWaveFunction.from_basis_state(n_qubits, initial_state, self.numbering)
elif isinstance(initial_state, str):
wave = QubitWaveFunction.from_string(string=initial_state).to_array()
wave = QubitWaveFunction.from_string(initial_state, self.numbering).to_array()
elif isinstance(initial_state, QubitWaveFunction):
wave = initial_state
elif isinstance(initial_state,np.ndarray):
wave = QubitWaveFunction.from_array(arr=initial_state, n_qubits=n_qubits) # silly but necessary
elif isinstance(initial_state, np.ndarray):
wave = QubitWaveFunction.from_array(initial_state, self.numbering) # silly but necessary
else:
raise TequilaQiboException('received an unusable initial state of type {}'.format(type(initial_state)))
state=wave.to_array()
result = circuit(state,nshots=samples)
state = wave.to_array()
result = circuit(state, nshots=samples)
else:
result = circuit(nshots=samples)


back = self.convert_measurements(backend_result=result)
return back

Expand Down
13 changes: 6 additions & 7 deletions src/tequila/simulators/simulator_qiskit.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,8 +300,7 @@ def do_simulate(self, variables, initial_state=0, *args, **kwargs) -> QubitWaveF

if initial_state != 0:
state = np.zeros(2 ** self.n_qubits)
initial_state = reverse_int_bits(initial_state, self.n_qubits)
state[initial_state] = 1.0
state[reverse_int_bits(initial_state, self.n_qubits)] = 1.0
init_circuit = qiskit.QuantumCircuit(self.q, self.c)
init_circuit.set_statevector(state)
circuit = init_circuit.compose(circuit)
Expand All @@ -310,7 +309,7 @@ def do_simulate(self, variables, initial_state=0, *args, **kwargs) -> QubitWaveF

backend_result = qiskit_backend.run(circuit, optimization_level=optimization_level).result()

return QubitWaveFunction.from_array(arr=backend_result.get_statevector(circuit), numbering=self.numbering)
return QubitWaveFunction.from_array(array=backend_result.get_statevector(circuit).data, numbering=self.numbering)

def do_sample(self, circuit: qiskit.QuantumCircuit, samples: int, read_out_qubits, *args,
**kwargs) -> QubitWaveFunction:
Expand Down Expand Up @@ -394,16 +393,16 @@ def convert_measurements(self, backend_result, target_qubits=None) -> QubitWaveF
measurements converted into wave function form.
"""
qiskit_counts = backend_result.result().get_counts()
result = QubitWaveFunction()
result = QubitWaveFunction(self.n_qubits, self.numbering)
# todo there are faster ways
for k, v in qiskit_counts.items():
converted_key = BitString.from_bitstring(other=BitStringLSB.from_binary(binary=k))
result._state[converted_key] = v
converted_key = BitString.from_binary(k)
result[converted_key] = v
if target_qubits is not None:
mapped_target = [self.qubit_map[q].number for q in target_qubits]
mapped_full = [self.qubit_map[q].number for q in self.abstract_qubits]
keymap = KeyMapRegisterToSubregister(subregister=mapped_target, register=mapped_full)
result = result.apply_keymap(keymap=keymap)
result = QubitWaveFunction.from_wavefunction(result, keymap, n_qubits=len(target_qubits))

return result

Expand Down
6 changes: 3 additions & 3 deletions src/tequila/simulators/simulator_qlm.py
Original file line number Diff line number Diff line change
Expand Up @@ -395,10 +395,10 @@ def do_simulate(self, variables, initial_state=0, *args, **kwargs) -> QubitWaveF
if MY_QLM:
result = PyLinalg().submit(job)
statevector = get_statevector(result)
return QubitWaveFunction.from_array(arr=statevector, numbering=self.numbering)
return QubitWaveFunction.from_array(array=statevector, numbering=self.numbering)

result = LinAlg().submit(job)
return QubitWaveFunction.from_array(arr=result.statevector, numbering=self.numbering)
return QubitWaveFunction.from_array(array=result.statevector, numbering=self.numbering)

def update_variables(self, variables):
"""
Expand Down Expand Up @@ -431,7 +431,7 @@ def convert_measurements(self, backend_result) -> QubitWaveFunction:
QubitWaveFunction:
measurements converted into wave function form.
"""
result = QubitWaveFunction()
result = QubitWaveFunction(self.n_qubits, self.numbering)
shots = int(backend_result.meta_data["nbshots"])
nbits = backend_result[0].qregs[0].length
for sample in backend_result:
Expand Down
21 changes: 7 additions & 14 deletions src/tequila/simulators/simulator_qulacs.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import warnings

from tequila import TequilaException, TequilaWarning
from tequila.utils.bitstrings import BitNumbering, BitString, BitStringLSB
from tequila.utils.bitstrings import BitNumbering, BitString, BitStringLSB, reverse_int_bits
from tequila.wavefunction.qubit_wavefunction import QubitWaveFunction
from tequila.simulators.simulator_base import BackendCircuit, BackendExpectationValue, QCircuit, change_basis
from tequila.utils.keymap import KeyMapRegisterToSubregister
Expand Down Expand Up @@ -149,11 +149,10 @@ def do_simulate(self, variables, initial_state, *args, **kwargs):
QubitWaveFunction representing result of the simulation.
"""
state = self.initialize_state(self.n_qubits)
lsb = BitStringLSB.from_int(initial_state, nbits=self.n_qubits)
state.set_computational_basis(BitString.from_binary(lsb.binary).integer)
state.set_computational_basis(reverse_int_bits(initial_state, self.n_qubits))
self.circuit.update_quantum_state(state)

wfn = QubitWaveFunction.from_array(arr=state.get_vector(), numbering=self.numbering)
wfn = QubitWaveFunction.from_array(array=state.get_vector(), numbering=self.numbering)
return wfn

def convert_measurements(self, backend_result, target_qubits=None) -> QubitWaveFunction:
Expand All @@ -170,22 +169,17 @@ def convert_measurements(self, backend_result, target_qubits=None) -> QubitWaveF
results transformed to tequila native QubitWaveFunction
"""

result = QubitWaveFunction()
result = QubitWaveFunction(self.n_qubits, self.numbering)
# todo there are faster ways


for k in backend_result:
converted_key = BitString.from_binary(BitStringLSB.from_int(integer=k, nbits=self.n_qubits).binary)
if converted_key in result._state:
result._state[converted_key] += 1
else:
result._state[converted_key] = 1
result[k] += 1

if target_qubits is not None:
mapped_target = [self.qubit_map[q].number for q in target_qubits]
mapped_full = [self.qubit_map[q].number for q in self.abstract_qubits]
keymap = KeyMapRegisterToSubregister(subregister=mapped_target, register=mapped_full)
result = result.apply_keymap(keymap=keymap)
result = QubitWaveFunction.from_wavefunction(result, keymap, n_qubits=len(target_qubits))

return result

Expand All @@ -212,8 +206,7 @@ def do_sample(self, samples, circuit, noise_model=None, initial_state=0, *args,
the results of sampling, as a Qubit Wave Function.
"""
state = self.initialize_state(self.n_qubits)
lsb = BitStringLSB.from_int(initial_state, nbits=self.n_qubits)
state.set_computational_basis(BitString.from_binary(lsb.binary).integer)
state.set_computational_basis(reverse_int_bits(initial_state, self.n_qubits))
circuit.update_quantum_state(state)
sampled = state.sampling(samples)
return self.convert_measurements(backend_result=sampled, target_qubits=self.measurements)
Expand Down
Loading

0 comments on commit ef3587d

Please sign in to comment.