-
Notifications
You must be signed in to change notification settings - Fork 131
Replace __fixed_parameters__
with analysis option
#734
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -47,6 +47,7 @@ | |
ExperimentData, | ||
AnalysisResultData, | ||
Options, | ||
AnalysisConfig, | ||
) | ||
|
||
PARAMS_ENTRY_PREFIX = "@Parameters_" | ||
|
@@ -233,13 +234,24 @@ class AnalysisExample(CurveAnalysis): | |
#: List[SeriesDef]: List of mapping representing a data series | ||
__series__ = list() | ||
|
||
#: List[str]: Fixed parameter in fit function. Value should be set to the analysis options. | ||
__fixed_parameters__ = list() | ||
|
||
def __init__(self): | ||
"""Initialize data fields that are privately accessed by methods.""" | ||
super().__init__() | ||
|
||
if hasattr(self, "__fixed_parameters__"): | ||
warnings.warn( | ||
"The class attribute __fixed_parameters__ has been deprecated and will be removed. " | ||
"Now this attribute is absorbed in analysis options as fixed_parameters. " | ||
"This warning will be dropped in v0.4 along with " | ||
"the support for the deprecated attribute.", | ||
DeprecationWarning, | ||
stacklevel=2, | ||
) | ||
# pylint: disable=no-member | ||
self._options.fixed_parameters = { | ||
p: self.options.get(p, None) for p in self.__fixed_parameters__ | ||
} | ||
|
||
#: Dict[str, Any]: Experiment metadata | ||
self.__experiment_metadata = None | ||
|
||
|
@@ -271,21 +283,12 @@ def _fit_params(cls) -> List[str]: | |
) | ||
|
||
# remove the first function argument. this is usually x, i.e. not a fit parameter. | ||
fit_params = list(list(fsigs)[0].parameters.keys())[1:] | ||
|
||
# remove fixed parameters | ||
if cls.__fixed_parameters__ is not None: | ||
for fixed_param in cls.__fixed_parameters__: | ||
try: | ||
fit_params.remove(fixed_param) | ||
except ValueError as ex: | ||
raise AnalysisError( | ||
f"Defined fixed parameter {fixed_param} is not a fit function argument." | ||
"Update series definition to ensure the parameter name is defined with " | ||
f"fit functions. Currently available parameters are {fit_params}." | ||
) from ex | ||
|
||
return fit_params | ||
return list(list(fsigs)[0].parameters.keys())[1:] | ||
|
||
@property | ||
def parameters(self) -> List[str]: | ||
"""Return parameters of this curve analysis.""" | ||
return [s for s in self._fit_params() if s not in self.options.fixed_parameters] | ||
|
||
@classmethod | ||
def _default_options(cls) -> Options: | ||
|
@@ -339,6 +342,9 @@ def _default_options(cls) -> Options: | |
as extra information. | ||
curve_fitter_options (Dict[str, Any]) Options that are passed to the | ||
specified curve fitting function. | ||
fixed_parameters (Dict[str, Any]): Fitting model parameters that are fixed | ||
during the curve fitting. This should be provided with default value | ||
keyed on one of the parameter names in the series definition. | ||
""" | ||
options = super()._default_options() | ||
|
||
|
@@ -360,11 +366,9 @@ def _default_options(cls) -> Options: | |
options.style = PlotterStyle() | ||
options.extra = dict() | ||
options.curve_fitter_options = dict() | ||
|
||
# automatically populate initial guess and boundary | ||
fit_params = cls._fit_params() | ||
options.p0 = {par_name: None for par_name in fit_params} | ||
options.bounds = {par_name: None for par_name in fit_params} | ||
options.p0 = {} | ||
options.bounds = {} | ||
options.fixed_parameters = {} | ||
|
||
return options | ||
|
||
|
@@ -754,16 +758,15 @@ def _run_analysis( | |
# | ||
|
||
# Update all fit functions in the series definitions if fixed parameter is defined. | ||
# Fixed parameters should be provided by the analysis options. | ||
if self.__fixed_parameters__: | ||
assigned_params = {k: self.options.get(k, None) for k in self.__fixed_parameters__} | ||
assigned_params = self.options.fixed_parameters | ||
|
||
if assigned_params: | ||
# Check if all parameters are assigned. | ||
if any(v is None for v in assigned_params.values()): | ||
raise AnalysisError( | ||
f"Unassigned fixed-value parameters for the fit " | ||
f"function {self.__class__.__name__}." | ||
f"All values of fixed-parameters, i.e. {self.__fixed_parameters__}, " | ||
f"All values of fixed-parameters, i.e. {assigned_params}, " | ||
"must be provided by the analysis options to run this analysis." | ||
) | ||
Comment on lines
+761
to
771
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can be a bit more concise and amend the code after this block too? if any(v is None for v in self.options.fixed_parameters.values()):
raise AnalysisError(f"All values of {self.options.fixed_parameters} must be provided.") then the line below can become if it is not too long: dict_def["fit_func"] = functools.partial(series_def.fit_func, **self.options.fixed_parameters) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes this is good suggestion but anyways this code block will be immediately removed by #726 so I just leave this as is |
||
|
||
|
@@ -815,7 +818,7 @@ def _run_analysis( | |
|
||
# Generate algorithmic initial guesses and boundaries | ||
default_fit_opt = FitOptions( | ||
parameters=self._fit_params(), | ||
parameters=self.parameters, | ||
default_p0=self.options.p0, | ||
default_bounds=self.options.bounds, | ||
**self.options.curve_fitter_options, | ||
|
@@ -964,6 +967,35 @@ def _run_analysis( | |
|
||
return analysis_results, figures | ||
|
||
@classmethod | ||
def from_config(cls, config: Union[AnalysisConfig, Dict]) -> "CurveAnalysis": | ||
nkanazawa1989 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# For backward compatibility. This will be removed in v0.4. | ||
|
||
instance = super().from_config(config) | ||
|
||
# When fixed param value is hard-coded as options. This is deprecated data structure. | ||
loaded_opts = instance.options.__dict__ | ||
|
||
# pylint: disable=no-member | ||
deprecated_fixed_params = { | ||
p: loaded_opts[p] for p in instance.parameters if p in loaded_opts | ||
} | ||
if any(deprecated_fixed_params): | ||
warnings.warn( | ||
"Fixed parameter value should be defined in options.fixed_parameters as " | ||
"a dictionary values, rather than a standalone analysis option. " | ||
"Please re-save this experiment to be loaded after deprecation period. " | ||
"This warning will be dropped in v0.4 along with " | ||
"the support for the deprecated fixed parameter options.", | ||
DeprecationWarning, | ||
stacklevel=2, | ||
) | ||
new_fixed_params = instance.options.fixed_parameters | ||
new_fixed_params.update(deprecated_fixed_params) | ||
instance.set_options(fixed_parameters=new_fixed_params) | ||
|
||
return instance | ||
|
||
|
||
def is_error_not_significant( | ||
val: Union[float, uncertainties.UFloat], | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -254,9 +254,10 @@ def __init__(self, qubit: int, backend: Optional[Backend] = None): | |
super().__init__([qubit], XGate(), backend=backend) | ||
# Set default analysis options | ||
self.analysis.set_options( | ||
angle_per_gate=np.pi, | ||
phase_offset=np.pi / 2, | ||
amp=1, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We no longer need to fix There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right, indeed this is not fixed. |
||
fixed_parameters={ | ||
"angle_per_gate": np.pi, | ||
"phase_offset": np.pi / 2, | ||
} | ||
) | ||
|
||
@classmethod | ||
|
@@ -291,8 +292,10 @@ def __init__(self, qubit: int, backend: Optional[Backend] = None): | |
super().__init__([qubit], SXGate(), backend=backend) | ||
# Set default analysis options | ||
self.analysis.set_options( | ||
angle_per_gate=np.pi / 2, | ||
phase_offset=np.pi, | ||
fixed_parameters={ | ||
"angle_per_gate": np.pi / 2, | ||
"phase_offset": np.pi, | ||
} | ||
) | ||
|
||
@classmethod | ||
|
@@ -354,9 +357,10 @@ def __init__(self, qubits: Sequence[int], backend: Optional[Backend] = None): | |
super().__init__(qubits, gate, backend=backend, measurement_qubits=[qubits[1]]) | ||
# Set default analysis options | ||
self.analysis.set_options( | ||
angle_per_gate=np.pi / 2, | ||
phase_offset=np.pi, | ||
amp=1, | ||
fixed_parameters={ | ||
"angle_per_gate": np.pi / 2, | ||
"phase_offset": np.pi, | ||
}, | ||
outcome="1", | ||
) | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hint for review. This cannot be initialized with parameter names, because fixed params are now instance attribute that user can modify after the instance is created. Note that now we have
.parameters
property, thus user can know what parameters live in the fit model.