Download model/QCPINN.py from OneScience-Group/QCPINN-CavityFlow: direct link, hf CLI and curl.
- Browser
- Download file 2.72 kB
-
https://huggingface.co/OneScience-Group/QCPINN-CavityFlow/resolve/main/model/QCPINN.py
- Command line
-
hf download hf://OneScience-Group/QCPINN-CavityFlow/model/QCPINN.py
-
curl -L -o QCPINN.py https://huggingface.co/OneScience-Group/QCPINN-CavityFlow/resolve/main/model/QCPINN.py
2.72 kB
| """Independent implementation of arXiv:2503.16678, Fig.2b and Tables 2/3. | |
| Inputs are physical (t,x,y), outputs (u,v,p). No reference fields enter forward. | |
| The three groups of n circuit angles are independent, as specified by 3*n*L. | |
| """ | |
| import pennylane as qml | |
| import torch | |
| from torch import nn | |
| class QCPINN(nn.Module): | |
| def __init__(self, input_dim=3, output_dim=3, hidden_dim=50, | |
| n_qubits=5, quantum_layers=1, backend="default.qubit", shots=None): | |
| super().__init__() | |
| if shots is not None: | |
| raise ValueError("The paper requires analytic simulation: shots=None") | |
| self.n_qubits = n_qubits | |
| self.quantum_layers = quantum_layers | |
| self.preprocessor = nn.Sequential(nn.Linear(input_dim, hidden_dim), nn.Tanh(), | |
| nn.Linear(hidden_dim, n_qubits)) | |
| self.quantum_angles = nn.Parameter(torch.empty(quantum_layers, 3*n_qubits)) | |
| self.postprocessor = nn.Sequential(nn.Linear(n_qubits, hidden_dim), nn.Tanh(), | |
| nn.Linear(hidden_dim, output_dim)) | |
| for block in (self.preprocessor, self.postprocessor): | |
| for module in block: | |
| if isinstance(module, nn.Linear): | |
| nn.init.xavier_normal_(module.weight) | |
| nn.init.zeros_(module.bias) | |
| nn.init.xavier_normal_(self.quantum_angles) | |
| device = qml.device(backend, wires=n_qubits, shots=None) | |
| def circuit(features, angles): | |
| qml.AngleEmbedding(features, wires=range(n_qubits), rotation="X") | |
| for layer in range(quantum_layers): | |
| for wire in range(n_qubits): | |
| qml.RX(angles[layer, wire], wires=wire) | |
| for wire in range(n_qubits): | |
| qml.RZ(angles[layer, n_qubits+wire], wires=wire) | |
| # Ring order matches Fig.2b, with distinct angles for every CRX. | |
| edges = [(n_qubits-1, 0)] + [(i-1, i) for i in range(n_qubits-1, 0, -1)] | |
| for index, edge in enumerate(edges): | |
| qml.CRX(angles[layer, 2*n_qubits+index], wires=edge) | |
| return tuple(qml.expval(qml.PauliZ(w)) for w in range(n_qubits)) | |
| self.circuit = circuit | |
| def forward(self, coordinates): | |
| if coordinates.ndim != 2 or coordinates.shape[1] != 3: | |
| raise ValueError(f"Expected (batch,3) (t,x,y); got {coordinates.shape}") | |
| encoded = self.preprocessor(coordinates) | |
| features = torch.stack(self.circuit(encoded, self.quantum_angles), dim=-1) | |
| return self.postprocessor(features.to(dtype=coordinates.dtype)) | |