pyGSTi icon indicating copy to clipboard operation
pyGSTi copied to clipboard

ValueError: output array is read-only on report generation

Open naezzell opened this issue 1 month ago • 3 comments

Describe the bug When trying to generate a GST report, I get the titular ValueError.

To Reproduce Steps to reproduce the behavior: Attempt to generate a GST report with latest develop branch. Not sure if I can share pickled datasets, so I will avoid that for now. If issue is unclear, I will have time later in the week to see if I can reproduce this error with a fake/example dataset.

Environment (please complete the following information):

  • pyGSTi version [0.9.14.1.post1.dev36+g8ed6a570a.develop.d20251209 (local install of develop branch in which this is the last commit)]
  • python version [3.13.9]
  • OS [e.g. OSX 15.5]

Additional context Here is the traceback, which is informative. Seems to have to do with eigenvalue computations?

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[4], line 15
     13 index = qsplit.index("comp")
     14 dirname = "./exp_gst_reports/" + "_".join(qsplit[index:])
---> 15 ro.write_html(dirname)

File ~/pygsti/pygsti/report/report.py:176, in Report.write_html(self, path, auto_open, link_to, connected, build_options, brevity, precision, resizable, autosize, single_file, verbosity)
    173     toggles['BrevityLT' + str(k + 1)] = True
    175 # Render sections
--> 176 qtys = self._build(build_options)
    178 # TODO this really should be a parameter of this method
    179 embed_figures = self._report_params.get('embed_figures', True)

File ~/pygsti/pygsti/report/report.py:91, in Report._build(self, build_options)
     89 qtys = self._global_qtys.copy()
     90 for section in self._sections:
---> 91     qtys.update(section.render(self._workspace, **full_params))
     93 return qtys

File ~/pygsti/pygsti/report/section/gauge.py:31, in GaugeInvariantsGatesSection.render(self, workspace, results, dataset_labels, est_labels, embed_figures, **kwargs)
     21 gi_switchboard = _create_single_metric_switchboard(
     22     workspace, results, True, dataset_labels, est_labels, embed_figures
     23 )
     24 gr_switchboard = _create_single_metric_switchboard(
     25     workspace, {}, False, [], embed_figures=embed_figures
     26 )
     28 return {
     29     'metricSwitchboard_gi': gi_switchboard,
     30     'metricSwitchboard_gr': gr_switchboard,
---> 31     **super().render(
     32         workspace, gr_switchboard=gr_switchboard,
     33         gi_switchboard=gi_switchboard,
     34         dataset_labels=dataset_labels, est_labels=est_labels,
     35         embed_figures=embed_figures, **kwargs
     36     )
     37 }

File ~/pygsti/pygsti/report/section/__init__.py:79, in Section.render(self, workspace, brevity, **kwargs)
     55 def render(self, workspace, brevity=0, **kwargs):
     56     """
     57     Render this section's figures.
     58 
   (...)     76         Key-value map of report quantities used for this section.
     77     """
     78     return {
---> 79         k: v(workspace, brevity=brevity, **kwargs)
     80         for k, v in self._figure_factories.items()
     81         if v.__figure_brevity_limit__ is None or brevity < v.__figure_brevity_limit__
     82     }

File ~/pygsti/pygsti/report/section/gauge.py:48, in GaugeInvariantsGatesSection.final_model_eigenvalue_table(workspace, switchboard, confidence_level, ci_brevity, **kwargs)
     46 @_Section.figure_factory(4)
     47 def final_model_eigenvalue_table(workspace, switchboard=None, confidence_level=None, ci_brevity=1, **kwargs):
---> 48     return workspace.GateEigenvalueTable(
     49         switchboard.mdl_gaugeinv, switchboard.mdl_target, _cri_gauge_inv(1, switchboard,
     50                                                                          confidence_level, ci_brevity),
     51         display=('evals', 'target', 'absdiff-evals', 'infdiff-evals', 'log-evals', 'absdiff-log-evals')
     52     )

File <string>:2, in factoryfn(model, target_model, confidence_region_info, display, virtual_ops)

File ~/pygsti/pygsti/report/workspacetables.py:2461, in GateEigenvalueTable.__init__(self, ws, model, target_model, confidence_region_info, display, virtual_ops)
   2411 def __init__(self, ws, model, target_model=None,
   2412              confidence_region_info=None,
   2413              display=('evals', 'rel', 'log-evals', 'log-rel', 'polar', 'relpolar'),
   2414              virtual_ops=None):
   2415     """
   2416     Create table which lists and displays (using a polar plot)
   2417     the eigenvalues of a model's gates.
   (...)   2459     ReportTable
   2460     """
-> 2461     super(GateEigenvalueTable, self).__init__(ws, self._create, model,
   2462                                               target_model,
   2463                                               confidence_region_info, display,
   2464                                               virtual_ops)

File ~/pygsti/pygsti/report/workspace.py:2050, in WorkspaceTable.__init__(self, ws, fn, *args)
   2047 self.tablefn = fn
   2048 self.initargs = args
   2049 self.tables, self.switchboards, self.sbSwitchIndices, self.switchpos_map = \
-> 2050     self.ws.switched_compute(self.tablefn, *self.initargs)

File ~/pygsti/pygsti/report/workspace.py:712, in Workspace.switched_compute(self, fn, *args)
    710         key = "NA"; result = v; break
    711 else:
--> 712     key, result = self.smartCache.cached_compute(fn, argVals)
    714 if key not in storedKeys or key == 'INEFFECTIVE':
    715     switchpos_map[pos] = len(resultValues)

File ~/pygsti/pygsti/baseobjs/smartcache.py:312, in SmartCache.cached_compute(self, fn, arg_vals, kwargs)
    310 self.typesigs[name_key] = typesig
    311 with _timed_block('call', times):
--> 312     self.cache[key] = fn(*arg_vals, **kwargs)
    313     if "_filledarrays" in special_kwargs:
    314         self.outargs[key] = tuple((arg_vals[i] if isinstance(i, int) else kwargs[i]
    315                                    for i in special_kwargs['_filledarrays']))  # copy?

File ~/pygsti/pygsti/report/workspacetables.py:2573, in GateEigenvalueTable._create(self, model, target_model, confidence_region_info, display, virtual_ops)
   2571 else:
   2572     mx = target_model.sim.product(gl)
-> 2573 target_evals = _tools.eigenvalues(mx)
   2575 if any([(x in display) for x in ('rel', 'log-rel', 'relpolar')]):
   2576     if isinstance(gl, _baseobjs.Label) or isinstance(gl, str):

File ~/pygsti/pygsti/tools/matrixtools.py:816, in eigenvalues(m, assume_hermitian, assume_normal)
    812     assume_hermitian = is_hermitian(m)
    813 if assume_hermitian:
    814     # Make sure it's Hermtian in exact arithmetic. This helps with
    815     # reproducibility across different implementations of LAPACK.
--> 816     m += m.T.conj()
    817     m /= 2
    818     return _np.linalg.eigvalsh(m)

ValueError: output array is read-only

naezzell avatar Dec 09 '25 19:12 naezzell