mindquantum.simulator.mqchem.sequential_ucc_ansatz 源代码

# Copyright 2025 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""Sequential UCC ansatz for MQ Chemistry simulator."""

from ...core.circuit import Circuit
from ...core.operators import FermionOperator
from ...utils.type_value_check import _check_int_type, _check_value_should_not_less
from .ucc_excitation_gate import UCCExcitationGate


[文档]class SequentialUCCAnsatz: """ Sequential Unitary Coupled-Cluster (UCC) Ansatz. This class allows constructing a UCC ansatz by adding excitation operators sequentially. Unlike ``FermionOperator`` which merges terms with the same index, this class preserves the order and distinctness of each added term, enabling the construction of multi-layer or Trotterized ansatzes where the same excitation operator may appear multiple times with different parameters. Note: The generated circuit uses :class:`~.simulator.mqchem.UCCExcitationGate`, which is intended for :class:`~.simulator.mqchem.MQChemSimulator`. Args: n_qubits (int): The number of qubits (spin-orbitals) in the simulation. Default: ``None``. n_electrons (int): The number of electrons of the given molecule. Default: ``None``. Examples: >>> from mindquantum.simulator import mqchem >>> from mindquantum.core.operators import FermionOperator >>> ansatz = mqchem.SequentialUCCAnsatz() >>> ansatz.append(FermionOperator("3^ 1", "a")) >>> ansatz.append(FermionOperator("4^ 2", "b")) >>> ansatz.append(FermionOperator("3^ 1", "c")) >>> print(len(ansatz.circuit)) 3 >>> print(ansatz.circuit.params_name) ['a', 'b', 'c'] """ def __init__(self, n_qubits=None, n_electrons=None): """Initialize a SequentialUCCAnsatz object.""" if n_qubits is not None: _check_int_type('n_qubits', n_qubits) _check_value_should_not_less('n_qubits', 1, n_qubits) if n_electrons is not None: _check_int_type('n_electrons', n_electrons) _check_value_should_not_less('n_electrons', 0, n_electrons) if n_qubits is not None and n_electrons is not None and n_electrons > n_qubits: raise ValueError( "The number of electrons must be smaller than the number of qubits (spin-orbitals) in the ansatz!" ) self.name = "Sequential UCC" self.n_qubits = n_qubits self._operators = [] self._circuit = Circuit() def _sanitize_operator(self, operator: FermionOperator) -> FermionOperator: """Validate operator invariants and return a detached canonical copy.""" if not isinstance(operator, FermionOperator): raise TypeError(f"operator must be a FermionOperator, but got {type(operator)}") operator_copied = FermionOperator(operator) if len(operator_copied.terms) != 1: raise ValueError("The added FermionOperator must have exactly one term.") operator_copied = operator_copied.normal_ordered() if len(operator_copied.terms) != 1: raise ValueError("The added FermionOperator must have exactly one non-zero term after normal ordering.") term = next(iter(operator_copied.terms.keys())) if not term: raise ValueError("The added FermionOperator cannot be an identity term.") qubits = [idx for idx, _ in term] if len(qubits) != len(set(qubits)): raise ValueError("The added FermionOperator must not contain duplicate indices in a term.") if self.n_qubits is not None and max(qubits) >= self.n_qubits: raise ValueError(f"Operator acts on qubit {max(qubits)}, which exceeds n_qubits ({self.n_qubits}).") return operator_copied
[文档] def append(self, operator: FermionOperator): """ Append a FermionOperator to the ansatz. Args: operator (FermionOperator): The excitation operator to add. Must have exactly one term. """ operator_copied = self._sanitize_operator(operator) self._operators.append(operator_copied) self._circuit += UCCExcitationGate(operator_copied)
def __iadd__(self, other): """ Support the += operator to append an operator. Args: other (FermionOperator): The operator to append. """ self.append(other) return self
[文档] def remove(self, index: int) -> FermionOperator: """ Remove and return the operator at the specified index. Args: index (int): The index of the operator to remove. Returns: FermionOperator, the removed operator. """ _check_int_type('index', index) if index < 0 or index >= len(self._operators): raise IndexError("Index out of range.") removed = self._operators.pop(index) self._rebuild_circuit() return FermionOperator(removed)
def _rebuild_circuit(self): """Reconstruct the circuit from the current list of operators.""" self._circuit = Circuit() for op in self._operators: self._circuit += UCCExcitationGate(op) def __len__(self): """Return the number of operators in the ansatz.""" return len(self._operators) def __getitem__(self, index): """Return the operator at the specified index.""" if isinstance(index, slice): return [FermionOperator(op) for op in self._operators[index]] _check_int_type('index', index) if index < -len(self._operators) or index >= len(self._operators): raise IndexError("Index out of range.") return FermionOperator(self._operators[index]) @property def circuit(self) -> Circuit: """Get the generated ansatz circuit.""" return self._circuit @property def operators(self): """Return the list of operators in the ansatz.""" return [FermionOperator(op) for op in self._operators]