from qiskit_cold_atom.fermions import FermionSimulator

# initialize the generic fermionic simulator backend
backend = FermionSimulator()

from qiskit import QuantumCircuit

circ1 = QuantumCircuit(4)  # Create a quantum circuit describing four fermionic modes.

circ1.fload([1, 2])  # Load fermions into modes 1 and 2.

circ1.draw(output="mpl", style="clifford")

circ1.measure_all()

job = backend.run(circ1)  # defaults to 1000 shots taken

print(job.result().get_counts())

circ2 = backend.initialize_circuit([1, 0, 1])

circ2.draw(output="mpl", style="clifford")

print(f"First circuit's register: {circ1.qregs}")
print(f"Second circuit's register: {circ2.qregs}")

from qiskit_nature.second_q.operators import FermionicOp
import numpy as np

# define the Hamiltonian as a FermionicOp
H_swap = np.pi / 2 * FermionicOp({"+_0 -_1": 1, "-_0 +_1": -1}, num_spin_orbitals=2)

from qiskit_cold_atom.fermions import FermionicGate

swap_fermions = FermionicGate(name="swap_fermion", num_modes=2, generator=H_swap)

circ = backend.initialize_circuit([1, 0, 1, 0])

circ.append(swap_fermions, qargs=[0, 1])
circ.append(swap_fermions, qargs=[2, 3])
circ.append(swap_fermions, qargs=[1, 2])

circ.measure_all()
# circ.draw(output='mpl', scale=0.8)

job = backend.run(circ)

print(job.result().get_counts())

# define a gate which will create superposition in the circuit
split_fermions = FermionicGate(name="split_fermion", num_modes=2, generator=H_swap / 2)

qc_sup = backend.initialize_circuit([1, 0, 0])

qc_sup.append(split_fermions, qargs=[0, 1])
qc_sup.append(swap_fermions, qargs=[1, 2])

qc_sup.measure_all()
qc_sup.draw(output="mpl", style="clifford", scale=0.8)

from qiskit.visualization import plot_histogram

job_sup = backend.run(qc_sup)

plot_histogram(job_sup.result().get_counts(), figsize=(5, 3))

qc = backend.initialize_circuit([1, 0, 0, 1])

qc.append(split_fermions, qargs=[0, 1])
qc.append(swap_fermions, qargs=[2, 3])
qc.append(swap_fermions, qargs=[0, 1])
qc.append(split_fermions, qargs=[2, 3])
qc.append(swap_fermions, qargs=[0, 2])

qc.measure_all()

job = backend.run(qc, shots=10, seed=1234)

# access the counts
print("counts :", job.result().get_counts())

# access the memory of individual outcomes
print("\nmemory :", job.result().get_memory())

# access the statevector
print("\nstatevector :", job.result().get_statevector())

# accedd the unitary
print("\ncircuit unitary : \n", job.result().get_unitary())

qc.draw(output="mpl", style="clifford")

print(backend.get_basis(qc))

corr = FermionicOp({"+_0 -_0 +_2 -_2": 1}, num_spin_orbitals=4) + FermionicOp(
    {"-_0 +_0 -_2 +_2": 1}, num_spin_orbitals=4
)

exp_val = backend.measure_observable_expectation(qc, observable=corr, shots=1000)

print(exp_val)

import numpy as np

from qiskit_cold_atom.fermions import (
    FfsimBackend,
    Hop,
    Interaction,
    Phase,
)

# initialize the ffsim backend
backend = FfsimBackend()

# set the number of orbitals and occupancies
norb = 8
nocc = norb // 4
nvrt = norb - nocc
occ_a = [1] * nocc + [0] * nvrt
occ_b = [1] * nocc + [0] * nvrt
occupations = [occ_a, occ_b]

# set parameters for fermionic gates
hopping = np.ones(norb - 1)
interaction = 1.0
mu = np.ones(norb)

# construct a circuit with some fermionic gates
circuit = backend.initialize_circuit(occupations)
circuit.append(Hop(2 * norb, hopping), list(range(2 * norb)))
circuit.append(Interaction(2 * norb, interaction), list(range(2 * norb)))
circuit.append(Phase(2 * norb, mu), list(range(2 * norb)))
circuit.measure_all()

# run the circuit and retrieve the measurement counts
job = backend.run(circuit, shots=10, seed=1234, num_species=2)

# access the counts
print("counts :", job.result().get_counts())

# access the memory of individual outcomes
print("\nmemory :", job.result().get_memory())

# print the length of the statevector
print("\nstatevector length :", len(job.result().get_statevector()))

import qiskit.tools.jupyter

%qiskit_version_table
%qiskit_copyright