Skip to content
Open
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
1 change: 1 addition & 0 deletions docs/_toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ parts:
- file: tutorials/generating_lattices
- file: tutorials/pulser-calc-example
- file: tutorials/finite-geometries
- file: tutorials/operators
- file: tutorials/vqe_qiskit
- caption: Use-Cases
chapters:
Expand Down
193 changes: 193 additions & 0 deletions docs/tutorials/operators.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "f2f2f320",
"metadata": {},
"source": [
"# The Operator and Operators classes\n",
"\n",
"The Operator and Operators classes provide a way to interact with Operators and Hamiltonians."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3b08d35b",
"metadata": {},
"outputs": [],
"source": [
"import qse"
]
},
{
"cell_type": "markdown",
"id": "a042dded",
"metadata": {},
"source": [
"## The Operator class\n",
"\n",
"The Operator class represents a single operator acting on a multiple qubit space.\n",
"\n",
"For example:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "82b62140",
"metadata": {},
"outputs": [],
"source": [
"# Create an operator acting with a Pauli X on the\n",
"# third qubit of a 4-qubit system with coefficient 1.0\n",
"qse.Operator(\"X\", qubits=2, nqbits=4, coef=1.0)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "cc3a8639",
"metadata": {},
"outputs": [],
"source": [
"# Create an operator acting with a Pauli Z on the\n",
"# first qubit and a Pauli Y on the second qubit of a 3-qubit system with coefficient 4.0\n",
"qse.Operator([\"Z\", \"Y\"], qubits=[0, 1], nqbits=3, coef=4.0)"
]
},
{
"cell_type": "markdown",
"id": "5ae6da3e",
"metadata": {},
"source": [
"The Operator class supports multiplication, where the coefficient will be multiplied by a scalar."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "90458598",
"metadata": {},
"outputs": [],
"source": [
"op = qse.Operator(\"X\", qubits=0, nqbits=2)\n",
"print(op)\n",
"\n",
"op *= 3.14\n",
"print(op)"
]
},
{
"cell_type": "markdown",
"id": "566f7566",
"metadata": {},
"source": [
"## The Operators class\n",
"\n",
"The Operators class is for representing a sum of Operator classes.\n",
"\n",
"For Example:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "960dd86d",
"metadata": {},
"outputs": [],
"source": [
"op1 = qse.Operator(\"X\", qubits=0, nqbits=2)\n",
"op2 = qse.Operator(\"Y\", qubits=1, nqbits=2)\n",
"ops = qse.Operators([op1, op2])\n",
"ops"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "96fe72f8",
"metadata": {},
"outputs": [],
"source": [
"ops = qse.Operators([qse.Operator(\"X\", [i, i + 1], nqbits=4) for i in range(3)])\n",
"ops"
]
},
{
"cell_type": "markdown",
"id": "cad1861a",
"metadata": {},
"source": [
"## Creating Operators from Qbits\n",
"Operators can be created from the Qbits class, given an interaction function."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7442e079",
"metadata": {},
"outputs": [],
"source": [
"spacing = 1.0\n",
"qbits = qse.lattices.ring(spacing, 12)\n",
"\n",
"\n",
"def distance_func(d):\n",
" # Nearest neighbours interaction\n",
" return -1.0 * (d < spacing * 1.01)\n",
"\n",
"\n",
"ham_int = qbits.compute_interaction_hamiltonian(\n",
" distance_func=distance_func, interaction=\"Z\"\n",
")\n",
"ham_int"
]
},
{
"cell_type": "markdown",
"id": "10fb5e24",
"metadata": {},
"source": [
"We can then combine this with other terms to make complex Hamiltonians. For example, the transverse Ising model:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "cb9ac4d4",
"metadata": {},
"outputs": [],
"source": [
"ham_ext = qse.Operators(\n",
" [qse.Operator(\"X\", i, nqbits=qbits.nqbits) for i in range(qbits.nqbits)]\n",
")\n",
"\n",
"ham = ham_int + ham_ext\n",
"ham"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "qse",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.13"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
31 changes: 31 additions & 0 deletions qse/operator/operator.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ def __init__(self, operator, qubits, nqbits, coef=1.0):

_check_operator(operator)

for q in qubits:
if q < 0 or q >= nqbits:
raise Exception(
"Qubit indices must be between 0 and nqbits-1, inclusive."
)

if len(qubits) != len(operator):
raise Exception(
"The number of passed qubits must equal the number of passed operators."
Expand Down Expand Up @@ -85,11 +91,36 @@ def to_qutip(self):
op[qi] = _qutip_converter(op_str)
return self.coef * qp.tensor(op)

def copy(self):
"""
Creates a copy of the operator.

Returns
-------
Operator
A new instance of Operator with the same attributes.
"""
return Operator(self.operator, self.qubits, self.nqbits, self.coef)

def __repr__(self):
return f"{self.coef:.2f} " + " ".join(
[f"{op}{q}" for op, q in zip(self.operator, self.qubits)]
)

def __mul__(self, other):
if isinstance(other, (int, float)):
return Operator(self.operator, self.qubits, self.nqbits, self.coef * other)
else:
raise NotImplementedError("Multiplication only supported for scalars.")

def __imul__(self, other):
if isinstance(other, (int, float)):
self.coef *= other
return self

def __rmul__(self, other):
return self.__mul__(other)


def _check_operator(op_list):
for op in op_list:
Expand Down
27 changes: 27 additions & 0 deletions qse/operator/operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,17 @@ def extend(self, other):
else:
raise Exception("other must be Operators or Operator.")

def copy(self):
"""
Creates a copy of the Operators object.

Returns
-------
Operators
A copy of the current Operators object.
"""
return Operators(self.operator_list.copy())

def __add__(self, other):
op = self.copy()
op += other
Expand All @@ -98,6 +109,22 @@ def __repr__(self):
def __getitem__(self, i):
return self.operator_list[i]

def __mul__(self, other):
if isinstance(other, (int, float)):
return Operators([op * other for op in self.operator_list])
else:
raise NotImplementedError("Multiplication only supported for scalars.")

def __imul__(self, other):
if isinstance(other, (int, float)):
self.operator_list = [op * other for op in self.operator_list]
return self
else:
raise NotImplementedError("Multiplication only supported for scalars.")

def __rmul__(self, other):
return self.__mul__(other)

@property
def nqbits(self):
return self[0].nqbits
Expand Down
36 changes: 36 additions & 0 deletions tests/qse/operator_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,27 @@ def test_operator_fail(qubits, operator):
qse.Operator(operator, qubits, nqubits=4)


@pytest.mark.parametrize("qubits", [-1, 4, [-1, 2], [3, 4]])
def test_operator_fail_qubit_index(qubits):
"""Test the class raises an error for a qubit outside the index range."""
with pytest.raises(Exception):
qse.Operator("Z", qubits, nqubits=4)


def test_operator_mul():
"""Test multiplying an Operator by a scalar."""
op = qse.Operator("X", 0, nqbits=2, coef=1.0)

op_scaled = op * 3.5

assert isinstance(op_scaled, qse.Operator)
assert op_scaled.to_str() == "XI"
assert np.isclose(op_scaled.coef, 3.5)

op *= 2.0
assert np.isclose(op.coef, 2.0)


def test_operators_init():
"""Test initializing empty Operators."""
ops = qse.Operators()
Expand Down Expand Up @@ -123,3 +144,18 @@ def test_operators_qutip():

op_sum = op1.to_qutip() + op2.to_qutip()
assert np.allclose(op_sum.full(), ops.to_qutip().full())


def test_operators_mul():
"""Test multiplying Operators by a scalar."""
nqbits = 4
ops = qse.Operators(
[qse.Operator("X", i, nqbits=nqbits + 5) for i in range(nqbits)]
)
assert all(np.isclose(op.coef, 1.0) for op in ops)

ops_scaled = ops * 2.5
assert all(np.isclose(op.coef, 2.5) for op in ops_scaled)

ops *= 3.5
assert all(np.isclose(op.coef, 3.5) for op in ops)
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading