vqe-pennylane 0.2.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,52 @@
1
+ """
2
+ common.geometry
3
+ ===============
4
+
5
+ Shared geometry generation for bond scans, angle scans, etc.
6
+ Used identically by VQE and QPE.
7
+ """
8
+
9
+ from __future__ import annotations
10
+ import numpy as np
11
+ from typing import Tuple, List
12
+
13
+ def generate_geometry(name: str, param: float):
14
+ """
15
+ Geometry wrapper.
16
+ Supported conventions (matching VQE):
17
+ "H2_BOND"
18
+ "H3+_BOND"
19
+ "LiH_BOND"
20
+ "H2O_ANGLE"
21
+ """
22
+ if name == "H2_BOND":
23
+ return ["H", "H"], np.array([
24
+ [0.0, 0.0, 0.0],
25
+ [0.0, 0.0, param]
26
+ ])
27
+
28
+ if name == "H3+_BOND":
29
+ # Example equilateral-ish geometry
30
+ return ["H", "H", "H"], np.array([
31
+ [0.0, 0.0, 0.0],
32
+ [param, 0.0, 0.0],
33
+ [0.5 * param, 0.866 * param, 0.0],
34
+ ])
35
+
36
+ if name == "LiH_BOND":
37
+ return ["Li", "H"], np.array([
38
+ [0.0, 0.0, 0.0],
39
+ [0.0, 0.0, param]
40
+ ])
41
+
42
+ if name == "H2O_ANGLE":
43
+ # Angle given in degrees
44
+ theta = np.deg2rad(param)
45
+ bond = 0.958
46
+ return ["O", "H", "H"], np.array([
47
+ [0.0, 0.0, 0.0],
48
+ [bond, 0.0, 0.0],
49
+ [bond * np.cos(theta), bond * np.sin(theta), 0.0],
50
+ ])
51
+
52
+ raise KeyError(f"Unknown geometry type: {name}")
@@ -0,0 +1,58 @@
1
+ """
2
+ common.hamiltonian
3
+ ==================
4
+
5
+ Shared Hamiltonian construction used by VQE, QPE, and future solvers.
6
+ """
7
+
8
+ from __future__ import annotations
9
+ import numpy as np
10
+ import pennylane as qml
11
+ from pennylane import qchem
12
+ from typing import Tuple
13
+
14
+ from vqe_qpe_common.molecules import get_molecule_config
15
+
16
+ def hartree_fock_state(symbols, charge, n_qubits):
17
+ """Compute electron count and return HF basis state bitstring."""
18
+ Z = {
19
+ "H": 1,
20
+ "He": 2,
21
+ "Li": 3,
22
+ "Be": 4,
23
+ "O": 8,
24
+ }
25
+ electrons = sum(Z[s] for s in symbols) - charge
26
+ return qchem.hf_state(int(electrons), n_qubits)
27
+
28
+ def build_hamiltonian(symbols, coordinates, charge, basis) -> Tuple[qml.Hamiltonian, int, np.ndarray]:
29
+ """
30
+ Build Hamiltonian + deduce HF state.
31
+ Returns: (Hamiltonian, n_qubits, hf_state)
32
+ """
33
+ try:
34
+ H, n_qubits = qchem.molecular_hamiltonian(
35
+ symbols=symbols,
36
+ coordinates=coordinates,
37
+ charge=charge,
38
+ basis=basis,
39
+ )
40
+ except Exception as e_primary:
41
+ print("⚠️ Default PennyLane-qchem backend failed — retrying with OpenFermion...")
42
+ try:
43
+ H, n_qubits = qchem.molecular_hamiltonian(
44
+ symbols=symbols,
45
+ coordinates=coordinates,
46
+ charge=charge,
47
+ basis=basis,
48
+ method="openfermion",
49
+ )
50
+ except Exception as e_fallback:
51
+ raise RuntimeError(
52
+ f"Failed to construct Hamiltonian.\n"
53
+ f"Primary error: {e_primary}\n"
54
+ f"Fallback error: {e_fallback}"
55
+ )
56
+
57
+ hf_state = hartree_fock_state(symbols, charge, n_qubits)
58
+ return H, n_qubits, hf_state
@@ -0,0 +1,107 @@
1
+ """
2
+ common.molecules
3
+ ================
4
+
5
+ Canonical molecule registry shared by VQE and QPE.
6
+
7
+ Every molecule entry contains:
8
+ • symbols (list[str])
9
+ • coordinates (np.ndarray)
10
+ • charge (int)
11
+ • basis (str)
12
+ """
13
+
14
+ from __future__ import annotations
15
+ import numpy as np
16
+
17
+ MOLECULES = {
18
+ "H2": {
19
+ "symbols": ["H", "H"],
20
+ "coordinates": np.array([
21
+ [0.0, 0.0, 0.0],
22
+ [0.0, 0.0, 0.7414],
23
+ ]),
24
+ "charge": 0,
25
+ "basis": "STO-3G",
26
+ },
27
+
28
+ "H3+": {
29
+ "symbols": ["H", "H", "H"],
30
+ "coordinates": np.array([
31
+ [0.0, 0.0, 0.0],
32
+ [0.0, 0.0, 0.872],
33
+ [0.755, 0.0, 0.436],
34
+ ]),
35
+ "charge": +1,
36
+ "basis": "STO-3G",
37
+ },
38
+
39
+ "LiH": {
40
+ "symbols": ["Li", "H"],
41
+ "coordinates": np.array([
42
+ [0.0, 0.0, 0.0],
43
+ [0.0, 0.0, 1.6],
44
+ ]),
45
+ "charge": 0,
46
+ "basis": "STO-3G",
47
+ },
48
+
49
+ "H2O": {
50
+ "symbols": ["O", "H", "H"],
51
+ "coordinates": np.array([
52
+ [0.000000, 0.000000, 0.000000],
53
+ [0.758602, 0.000000, 0.504284],
54
+ [-0.758602, 0.000000, 0.504284],
55
+ ]),
56
+ "charge": 0,
57
+ "basis": "STO-3G",
58
+ },
59
+
60
+ # ------------------------------------------------------
61
+ # NEW MOLECULES (BeH2, H4-chain, HeH+)
62
+ # ------------------------------------------------------
63
+
64
+ "HeH+": {
65
+ "symbols": ["He", "H"],
66
+ # Typical HeH+ bond length ~1.46 Å (varies in literature)
67
+ "coordinates": np.array([
68
+ [0.0, 0.0, 0.0],
69
+ [0.0, 0.0, 1.46],
70
+ ]),
71
+ "charge": +1,
72
+ "basis": "STO-3G",
73
+ },
74
+
75
+ "BeH2": {
76
+ "symbols": ["Be", "H", "H"],
77
+ # Linear geometry: H–Be–H with ~1.33 Å Be–H bond length
78
+ "coordinates": np.array([
79
+ [0.0, 0.0, 0.0],
80
+ [0.0, 0.0, 1.33],
81
+ [0.0, 0.0, -1.33],
82
+ ]),
83
+ "charge": 0,
84
+ "basis": "STO-3G",
85
+ },
86
+
87
+ "H4": {
88
+ "symbols": ["H", "H", "H", "H"],
89
+ # Linear H4 chain, equally spaced at 1.0 Å
90
+ "coordinates": np.array([
91
+ [0.0, 0.0, 0.0],
92
+ [1.0, 0.0, 0.0],
93
+ [2.0, 0.0, 0.0],
94
+ [3.0, 0.0, 0.0],
95
+ ]),
96
+ "charge": 0,
97
+ "basis": "STO-3G",
98
+ },
99
+ }
100
+
101
+
102
+ def get_molecule_config(name: str):
103
+ """Return the molecule configuration dict."""
104
+ try:
105
+ return MOLECULES[name]
106
+ except KeyError:
107
+ raise KeyError(f"Unknown molecule '{name}'. Available = {list(MOLECULES.keys())}")
@@ -0,0 +1,167 @@
1
+ """
2
+ common.plotting
3
+ ================
4
+
5
+ Centralised plotting utilities for the entire VQE/QPE package.
6
+
7
+ Provides:
8
+ - Unified filename builder for all PNG plots
9
+ - Automatic path sanitisation (molecule names, operators, symbols)
10
+ - Consistent plotting export (DPI, bbox, tight layout)
11
+ - Single point of control for changing any plotting behaviour
12
+
13
+ This ensures:
14
+ • Zero filename collisions
15
+ • Fully consistent naming between VQE, QPE, and notebooks
16
+ • Clean, readable filenames for publication-quality figures
17
+ """
18
+
19
+ from __future__ import annotations
20
+ import os
21
+ from typing import Dict, Optional
22
+ import matplotlib.pyplot as plt
23
+
24
+
25
+ # ---------------------------------------------------------------------
26
+ # Base Directories
27
+ # ---------------------------------------------------------------------
28
+ BASE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
29
+ IMG_DIR = os.path.join(BASE_DIR, "images")
30
+
31
+
32
+ def ensure_plot_dirs():
33
+ """Ensure required directories exist."""
34
+ os.makedirs(IMG_DIR, exist_ok=True)
35
+
36
+
37
+ # ---------------------------------------------------------------------
38
+ # Name Sanitisation
39
+ # ---------------------------------------------------------------------
40
+ def format_molecule_name(mol: str) -> str:
41
+ """
42
+ Convert a molecule name into a filesystem-safe token.
43
+
44
+ Examples:
45
+ "H3+" → "H3plus"
46
+ "H2 O" → "H2_O"
47
+ "LiH" → "LiH"
48
+ """
49
+ mol = mol.replace("+", "plus")
50
+ mol = mol.replace(" ", "_")
51
+ return mol
52
+
53
+
54
+ def format_token(val: Optional[str | float | int]) -> Optional[str]:
55
+ """
56
+ Convert metadata values into clean filename components.
57
+
58
+ Examples:
59
+ 0.05 → "0p05"
60
+ 0.0 → "0p0"
61
+ "Adam" → "Adam"
62
+ None → None
63
+ """
64
+ if val is None:
65
+ return None
66
+ if isinstance(val, (int, float)):
67
+ s = f"{val:.5f}".rstrip("0").rstrip(".")
68
+ return s.replace(".", "p")
69
+ return str(val).replace(" ", "_")
70
+
71
+
72
+ # ---------------------------------------------------------------------
73
+ # Filename Builder
74
+ # ---------------------------------------------------------------------
75
+ def build_filename(
76
+ molecule: Optional[str] = None,
77
+ *,
78
+ topic: str,
79
+ extras: Optional[Dict[str, Optional[float | int | str]]] = None,
80
+ ) -> str:
81
+ """
82
+ Build a descriptive, collision-safe PNG filename.
83
+
84
+ Parameters
85
+ ----------
86
+ molecule : str, optional
87
+ Molecule identifier (e.g., "H2", "H3+").
88
+ topic : str
89
+ The major theme of the plot, e.g.
90
+ "vqe_convergence", "qpe_distribution",
91
+ "noise_sweep", "optimizer_comparison"
92
+ extras : dict, optional
93
+ Arbitrary metadata to encode in filename, e.g.
94
+ {"optimizer": "Adam", "ansatz": "UCCSD", "anc": 4}
95
+
96
+ Returns
97
+ -------
98
+ str
99
+ Filename ending in `.png`.
100
+
101
+ Example
102
+ -------
103
+ build_filename(
104
+ molecule="H2",
105
+ topic="qpe_distribution",
106
+ extras={"anc": 4, "pdep": 0.02}
107
+ )
108
+
109
+ → "H2_qpe_distribution_anc4_pdep0p02.png"
110
+ """
111
+ parts = []
112
+
113
+ if molecule:
114
+ parts.append(format_molecule_name(molecule))
115
+
116
+ # Topic (safe)
117
+ topic = topic.lower().replace(" ", "_")
118
+ parts.append(topic)
119
+
120
+ # Append metadata tokens
121
+ if extras:
122
+ for key, val in extras.items():
123
+ fv = format_token(val)
124
+ if fv is not None:
125
+ parts.append(f"{key}{fv}")
126
+
127
+ return "_".join(parts) + ".png"
128
+
129
+
130
+ # ---------------------------------------------------------------------
131
+ # Unified Save
132
+ # ---------------------------------------------------------------------
133
+ def save_plot(filename: str, show: bool = True) -> str:
134
+ """
135
+ Save the current matplotlib figure to the global IMG_DIR and optionally
136
+ display it inline (for notebooks).
137
+
138
+ Parameters
139
+ ----------
140
+ filename : str
141
+ A clean filename produced by build_filename().
142
+ show : bool, default=True
143
+ If True, display the plot (for Jupyter/VSCode notebooks).
144
+ If False, only save.
145
+
146
+ Returns
147
+ -------
148
+ str
149
+ Absolute path to the saved PNG.
150
+ """
151
+ ensure_plot_dirs()
152
+
153
+ # Ensure .png suffix
154
+ if not filename.lower().endswith(".png"):
155
+ filename = filename + ".png"
156
+
157
+ path = os.path.join(IMG_DIR, filename)
158
+
159
+ plt.savefig(path, dpi=300, bbox_inches="tight")
160
+
161
+ if show:
162
+ plt.show()
163
+ else:
164
+ plt.close()
165
+
166
+ print(f"📁 Saved plot → {path}")
167
+ return path