mirror of
https://github.com/vale981/hopsflow
synced 2025-03-05 08:51:37 -05:00
Merge remote-tracking branch 'origin/online_calculation'
This commit is contained in:
commit
88ca1024e0
2 changed files with 146 additions and 39 deletions
|
@ -18,6 +18,7 @@ from numpy.typing import NDArray
|
||||||
import opt_einsum as oe
|
import opt_einsum as oe
|
||||||
from hops.core.hierarchy_data import HIData
|
from hops.core.hierarchy_data import HIData
|
||||||
from hops.core.hierarchy_parameters import HIParams
|
from hops.core.hierarchy_parameters import HIParams
|
||||||
|
from typing import Callable
|
||||||
import copy
|
import copy
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
@ -498,10 +499,37 @@ def heat_flow_ensemble(
|
||||||
:returns: the value of the flow for each time step
|
:returns: the value of the flow for each time step
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if therm_args is None and only_therm:
|
flow_worker = make_heat_flow_worker(
|
||||||
raise ValueError("Can't calculate only thermal part if therm_args are None.")
|
params, therm_args[1] if therm_args else None, only_therm
|
||||||
|
)
|
||||||
|
|
||||||
thermal = therm_args[1] if therm_args else None
|
return util.ensemble_mean(
|
||||||
|
iter(zip(ψ_0s, ψ_1s, therm_args[0]))
|
||||||
|
if therm_args
|
||||||
|
else iter(zip(ψ_0s, ψ_1s, itertools.repeat(0))),
|
||||||
|
flow_worker,
|
||||||
|
**kwargs,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def make_heat_flow_worker(
|
||||||
|
params: SystemParams,
|
||||||
|
thermal: Optional[ThermalParams] = None,
|
||||||
|
only_therm: bool = False,
|
||||||
|
) -> Callable[..., np.ndarray]:
|
||||||
|
"""Constructs a worker function that takes ``(first hierarchy
|
||||||
|
state, aux states, thermal process seed)`` and returns the flow.
|
||||||
|
|
||||||
|
:param params: a parameter object for the system, see
|
||||||
|
:any:`SystemParams`
|
||||||
|
:param therm: the parameter object for the nonzero temperature,
|
||||||
|
see :any:`ThermalParams`
|
||||||
|
:param only_therm: whether to only calculate the thermal part of
|
||||||
|
the flow
|
||||||
|
"""
|
||||||
|
|
||||||
|
if thermal is None and only_therm:
|
||||||
|
raise ValueError("Can't calculate only thermal part if therm_args are None.")
|
||||||
|
|
||||||
def flow_worker(ψs: tuple[np.ndarray, np.ndarray, int]):
|
def flow_worker(ψs: tuple[np.ndarray, np.ndarray, int]):
|
||||||
ψ_0, ψ_1, seed = ψs
|
ψ_0, ψ_1, seed = ψs
|
||||||
|
@ -519,13 +547,7 @@ def heat_flow_ensemble(
|
||||||
|
|
||||||
return flow
|
return flow
|
||||||
|
|
||||||
return util.ensemble_mean(
|
return flow_worker
|
||||||
iter(zip(ψ_0s, ψ_1s, therm_args[0]))
|
|
||||||
if therm_args
|
|
||||||
else iter(zip(ψ_0s, ψ_1s, itertools.repeat(0))),
|
|
||||||
flow_worker,
|
|
||||||
**kwargs,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def heat_flow_from_data(
|
def heat_flow_from_data(
|
||||||
|
@ -563,6 +585,34 @@ def heat_flow_from_data(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def make_interaction_worker(
|
||||||
|
params: SystemParams,
|
||||||
|
thermal: Optional[ThermalParams] = None,
|
||||||
|
power: bool = False,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
:param params: a parameter object for the system, see
|
||||||
|
:any:`SystemParams`
|
||||||
|
:param thermal: the thermal object, see :any:`ThermalParams`
|
||||||
|
:param power: whether to calculate the interaction power
|
||||||
|
"""
|
||||||
|
|
||||||
|
def interaction_energy_task(
|
||||||
|
ψs: Tuple[np.ndarray, np.ndarray, int],
|
||||||
|
) -> np.ndarray:
|
||||||
|
ψ_0, ψ_1, seeds = ψs
|
||||||
|
run = HOPSRun(ψ_0, ψ_1, params, power) # type: ignore
|
||||||
|
energy = interaction_energy_coupling(run, params) # type: ignore
|
||||||
|
|
||||||
|
if thermal is not None:
|
||||||
|
therm_run = ThermalRunParams(thermal, seeds) # type: ignore
|
||||||
|
energy += interaction_energy_therm(run, therm_run)
|
||||||
|
|
||||||
|
return energy
|
||||||
|
|
||||||
|
return interaction_energy_task
|
||||||
|
|
||||||
|
|
||||||
def interaction_energy_ensemble(
|
def interaction_energy_ensemble(
|
||||||
ψ_0s: Iterator[np.ndarray],
|
ψ_0s: Iterator[np.ndarray],
|
||||||
ψ_1s: Iterator[np.ndarray],
|
ψ_1s: Iterator[np.ndarray],
|
||||||
|
@ -589,20 +639,7 @@ def interaction_energy_ensemble(
|
||||||
|
|
||||||
thermal = therm_args[1] if therm_args else None
|
thermal = therm_args[1] if therm_args else None
|
||||||
|
|
||||||
def interaction_energy_task(
|
interaction_energy_task = make_interaction_worker(params, thermal, power)
|
||||||
ψs: Tuple[np.ndarray, np.ndarray, int],
|
|
||||||
) -> np.ndarray:
|
|
||||||
ψ_0, ψ_1, seeds = ψs
|
|
||||||
|
|
||||||
run = HOPSRun(ψ_0, ψ_1, params, power) # type: ignore
|
|
||||||
energy = interaction_energy_coupling(run, params) # type: ignore
|
|
||||||
|
|
||||||
if thermal is not None:
|
|
||||||
therm_run = ThermalRunParams(thermal, seeds) # type: ignore
|
|
||||||
energy += interaction_energy_therm(run, therm_run)
|
|
||||||
|
|
||||||
return energy
|
|
||||||
|
|
||||||
return util.ensemble_mean(
|
return util.ensemble_mean(
|
||||||
iter(zip(ψ_0s, ψ_1s, therm_args[0]))
|
iter(zip(ψ_0s, ψ_1s, therm_args[0]))
|
||||||
if therm_args
|
if therm_args
|
||||||
|
|
100
hopsflow/util.py
100
hopsflow/util.py
|
@ -28,6 +28,7 @@ import opt_einsum as oe
|
||||||
import gc
|
import gc
|
||||||
import math
|
import math
|
||||||
import time
|
import time
|
||||||
|
import pickle
|
||||||
from hops.core.hierarchy_data import HIData
|
from hops.core.hierarchy_data import HIData
|
||||||
|
|
||||||
Aggregate = tuple[int, np.ndarray, np.ndarray]
|
Aggregate = tuple[int, np.ndarray, np.ndarray]
|
||||||
|
@ -490,27 +491,18 @@ def operator_expectation(
|
||||||
return expect
|
return expect
|
||||||
|
|
||||||
|
|
||||||
def operator_expectation_ensemble(
|
def make_operator_expectation_task(
|
||||||
ψs: Iterator[np.ndarray],
|
|
||||||
op: Union[np.ndarray, DynamicMatrix],
|
op: Union[np.ndarray, DynamicMatrix],
|
||||||
t: np.ndarray,
|
t: np.ndarray,
|
||||||
normalize: bool = False,
|
normalize: bool = False,
|
||||||
real: bool = False,
|
real: bool = False,
|
||||||
**kwargs,
|
):
|
||||||
) -> EnsembleValue:
|
|
||||||
"""Calculates the expecation value of ``op`` as a time series.
|
|
||||||
|
|
||||||
:param ψs: A collection of stochastic trajectories. Each
|
|
||||||
element should have the shape ``(time, dim-sys)``.
|
|
||||||
:param op: The operator.
|
|
||||||
:param N: Number of samples to take.
|
|
||||||
:param real: Whether to take the real part.
|
|
||||||
|
|
||||||
All the other kwargs are passed on to :any:`ensemble_mean`.
|
|
||||||
|
|
||||||
:returns: the expectation value
|
|
||||||
"""
|
"""
|
||||||
|
Returns a function that takes the first hierarchy states and
|
||||||
|
returns the expectation value of the operator op.
|
||||||
|
|
||||||
|
For the arguments see :any:`operator_expectation_ensemble`.
|
||||||
|
"""
|
||||||
if isinstance(op, ConstantMatrix):
|
if isinstance(op, ConstantMatrix):
|
||||||
op = op(0)
|
op = op(0)
|
||||||
|
|
||||||
|
@ -542,6 +534,31 @@ def operator_expectation_ensemble(
|
||||||
|
|
||||||
return sandwhiches
|
return sandwhiches
|
||||||
|
|
||||||
|
return op_exp_task
|
||||||
|
|
||||||
|
|
||||||
|
def operator_expectation_ensemble(
|
||||||
|
ψs: Iterator[np.ndarray],
|
||||||
|
op: Union[np.ndarray, DynamicMatrix],
|
||||||
|
t: np.ndarray,
|
||||||
|
normalize: bool = False,
|
||||||
|
real: bool = False,
|
||||||
|
**kwargs,
|
||||||
|
) -> EnsembleValue:
|
||||||
|
"""Calculates the expecation value of ``op`` as a time series.
|
||||||
|
|
||||||
|
:param ψs: A collection of stochastic trajectories. Each
|
||||||
|
element should have the shape ``(time, dim-sys)``.
|
||||||
|
:param op: The operator.
|
||||||
|
:param N: Number of samples to take.
|
||||||
|
:param real: Whether to take the real part.
|
||||||
|
|
||||||
|
All the other kwargs are passed on to :any:`ensemble_mean`.
|
||||||
|
|
||||||
|
:returns: the expectation value
|
||||||
|
"""
|
||||||
|
|
||||||
|
op_exp_task = make_operator_expectation_task(op, t, normalize, real)
|
||||||
return ensemble_mean(ψs, op_exp_task, **kwargs)
|
return ensemble_mean(ψs, op_exp_task, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
@ -641,6 +658,9 @@ class WelfordAggregator:
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def sample_variance(self) -> np.ndarray:
|
def sample_variance(self) -> np.ndarray:
|
||||||
|
if self.n == 1:
|
||||||
|
return np.zeros_like(self.mean)
|
||||||
|
|
||||||
return self._m_2 / (self.n - 1)
|
return self._m_2 / (self.n - 1)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -651,6 +671,10 @@ class WelfordAggregator:
|
||||||
def ensemble_std(self) -> np.ndarray:
|
def ensemble_std(self) -> np.ndarray:
|
||||||
return np.sqrt(self.ensemble_variance)
|
return np.sqrt(self.ensemble_variance)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ensemble_value(self) -> EnsembleValue:
|
||||||
|
return EnsembleValue([(self.n, self.mean, self.ensemble_std)])
|
||||||
|
|
||||||
|
|
||||||
class JSONEncoder(json.JSONEncoder):
|
class JSONEncoder(json.JSONEncoder):
|
||||||
"""
|
"""
|
||||||
|
@ -708,6 +732,52 @@ def _ensemble_remote_function(function, chunk: tuple, index: int):
|
||||||
return res, index
|
return res, index
|
||||||
|
|
||||||
|
|
||||||
|
def get_online_data_path(save: str):
|
||||||
|
return Path("results") / Path(f"online_{save}.npy")
|
||||||
|
|
||||||
|
|
||||||
|
def load_online_cache(save: str):
|
||||||
|
path = get_online_data_path(save)
|
||||||
|
|
||||||
|
with path.open("rb") as agg_file:
|
||||||
|
aggregate = pickle.load(agg_file)
|
||||||
|
|
||||||
|
return aggregate.ensemble_value
|
||||||
|
|
||||||
|
|
||||||
|
def ensemble_mean_online(
|
||||||
|
args: Any,
|
||||||
|
save: str,
|
||||||
|
function: Callable[..., np.ndarray],
|
||||||
|
) -> Optional[EnsembleValue]:
|
||||||
|
path = get_online_data_path(save)
|
||||||
|
|
||||||
|
if args is None:
|
||||||
|
result = None
|
||||||
|
else:
|
||||||
|
result = function(args)
|
||||||
|
|
||||||
|
if np.isnan(np.sum(result)):
|
||||||
|
result = None
|
||||||
|
|
||||||
|
if path.exists():
|
||||||
|
with path.open("rb") as agg_file:
|
||||||
|
aggregate = pickle.load(agg_file)
|
||||||
|
if result is not None:
|
||||||
|
aggregate.update(result)
|
||||||
|
|
||||||
|
else:
|
||||||
|
if result is None:
|
||||||
|
raise RuntimeError("No cache and no result.")
|
||||||
|
|
||||||
|
aggregate = WelfordAggregator(result)
|
||||||
|
|
||||||
|
with path.open("wb") as agg_file:
|
||||||
|
pickle.dump(aggregate, agg_file)
|
||||||
|
|
||||||
|
return aggregate.ensemble_value
|
||||||
|
|
||||||
|
|
||||||
def ensemble_mean(
|
def ensemble_mean(
|
||||||
arg_iter: Iterator[Any],
|
arg_iter: Iterator[Any],
|
||||||
function: Callable[..., np.ndarray],
|
function: Callable[..., np.ndarray],
|
||||||
|
|
Loading…
Add table
Reference in a new issue