Skip to content
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

Refactor QubitWaveFunction, add support for Numpy array backed dense representation #370

Merged
merged 4 commits into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
4 changes: 2 additions & 2 deletions src/tequila/quantumchemistry/encodings.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,9 @@ 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(n_qubits, 0)
wfn = wfn.apply_qubitoperator(operator=op)
assert (len(wfn.keys()) == 1)
assert wfn.length() == 1
key = list(wfn.keys())[0].array
return key

Expand Down
6 changes: 3 additions & 3 deletions src/tequila/simulators/simulator_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
HAS_QISKIT_GPU = True
INSTALLED_SIMULATORS["qiskit_gpu"] = BackendTypes(BackendCircuitQiskitGpu, BackendExpectationValueQiskitGpu)
INSTALLED_SAMPLERS["qiskit_gpu"] = BackendTypes(BackendCircuitQiskitGpu, BackendExpectationValueQiskitGpu)
from tequila.simulators.simulator_qiskit_gpu import HAS_NOISE as HAS_QISKIT_GPU_NOISE
from tequila.simulators.simulator_qiskit import HAS_NOISE as HAS_QISKIT_GPU_NOISE
if HAS_QISKIT_GPU_NOISE:
INSTALLED_NOISE_SAMPLERS["qiskit_gpu"] = BackendTypes(BackendCircuitQiskitGpu, BackendExpectationValueQiskitGpu)
except (ImportError, DistributionNotFound):
Expand Down 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
4 changes: 2 additions & 2 deletions src/tequila/simulators/simulator_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,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 initial_state.length() != 1:
raise TequilaException("only product states as initial states accepted")
initial_state = list(initial_state.keys())[0].integer

Expand All @@ -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 initial_state.length() != 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
14 changes: 7 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,17 @@ 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
# Qiskit uses LSB bitstrings, but from_binary expects MSB
converted_key = BitString.from_binary(k[::-1])
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
Loading