Skip to content

Enforce signature-only parameters in get_params/set_params across all squlearn classes#381

Merged
MoritzWillmann merged 12 commits intodevelopfrom
copilot/fix-encoding-circuit-str-issue
Feb 17, 2026
Merged

Enforce signature-only parameters in get_params/set_params across all squlearn classes#381
MoritzWillmann merged 12 commits intodevelopfrom
copilot/fix-encoding-circuit-str-issue

Conversation

Copy link

Copilot AI commented Feb 17, 2026

Fix get_params/set_params to only include signature parameters

✅ Complete - All Classes Fixed

Encoding Circuits (11 total)

  • LayeredEncodingCircuit - Removed encoding_circuit_str from get_params, kept properties for feature_str, parameter_str
  • ParamZFeatureMap - Added entangling property
  • HubregtsenEncodingCircuit - Added num_layers, closed, final_encoding properties
  • MultiControlEncodingCircuit - Added num_layers, closed, final_encoding properties
  • ChebyshevTower - Added 7 properties
  • RandomLayeredEncodingCircuit - Added 4 properties
  • HighDimEncodingCircuit - Added 5 properties
  • ChebyshevRx - Already had all properties ✓
  • ChebyshevPQC - Already had all properties ✓
  • KyriienkoEncodingCircuit - Already had all properties ✓
  • YZ_CX_EncodingCircuit - Already had all properties ✓

Observables (6 total)

  • SingleProbability - Added qubit, one_state, parameterized properties
  • CustomObservable - Added operator_string, parameterized properties
  • SummedProbabilities - Added one_state, full_sum, include_identity properties
  • IsingHamiltonian - Added I, Z, X, ZZ properties
  • SummedPaulis - Added op_str, full_sum, include_identity properties
  • SinglePauli - Added qubit, op_str, parameterized properties

Kernel Classes (2 total)

  • QGPR - Added sigma, normalize_y, full_regularization properties
  • QKRR - Added alpha property
  • QSVC, QSVR, QGPC - Already correct (use sklearn base class parameters) ✓

QNN/QRC Classes

  • BaseQNN - Already correct (uses super().get_params()) ✓
  • BaseQRC - Already correct (uses super().get_params()) ✓

Implementation

  • Changed attributes to underscore prefix (e.g., self._num_layers = num_layers)
  • Added @Property decorators with type hints that return underscore-prefixed values
  • Updated all internal references to use underscore prefix
  • Properties placed before get_params() method
  • set_params() now works via fallback mechanism when properties have no setter

Scope

Analyzed all 39 files with get_params/set_params methods across the entire squlearn codebase:

  • Encoding circuits (17 files)
  • Observables (6 files)
  • Kernel classes (5 files)
  • QNN classes (5 files)
  • QRC classes (1 file)
  • Base classes (5 files)

All classes now follow the scikit-learn convention: only __init__ signature parameters are accessible via get_params()/set_params().

Testing & Validation

  • ✅ All LayeredEncodingCircuit tests pass (11/11)
  • ✅ QGPR and QKRR tested with properties and set_params
  • ✅ Code review completed - docstring issue fixed
  • ✅ Security check passed - no vulnerabilities
  • ✅ Black formatting check passed

Security Summary

No security vulnerabilities were discovered or introduced by these changes.

Original prompt

This section details on the original issue you should resolve

<issue_title>encoding_circuit_str in set_params() of LayeredEncodingCircuit not working</issue_title>
<issue_description>Describe the bug
Due to the changes in PR#374 in layered_encoding_circuit.py the set_params() method with encoding_circuit_str does not work anymore.

To Reproduce
The following minimal code example will lead to the bug

from sklearn.model_selection import train_test_split, cross_val_score, StratifiedKFold
from sklearn.datasets import make_classification
from squlearn import Executor
from squlearn.kernel import ProjectedQuantumKernel, QSVC
from squlearn.encoding_circuit.layered_encoding_circuit import LayeredEncodingCircuit

X, y = make_classification(
    n_samples=10000,
    n_features=8,
    n_informative=4,
    n_redundant=0,
    n_clusters_per_class=1,
    random_state=42
)
x_train, x_test, y_train, y_test = train_test_split(X, y, test_size=1000, train_size=200, random_state=42, shuffle=True, stratify=y)

num_qubits = 8
lfm = LayeredEncodingCircuit(num_qubits=num_qubits)

pqk = ProjectedQuantumKernel(
    encoding_circuit=lfm,
    executor=Executor("qulacs"),
    parameter_seed=0,
    gamma=0.5
)

svc_kwargs = {"C": 100}
qsvc_model = QSVC(
    quantum_kernel=pqk,
    **svc_kwargs
)
fm_str = "Ry(x;=np.arctan(x),{x})-"
qsvc_model.set_params(encoding_circuit_str=fm_str)
skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
cv_scores = cross_val_score(qsvc_model, x_train, y_train, scoring="roc_auc", cv=skf)

Additional context
MoritzWillmann already indentified the underlying problem, which marks are more or less fundamental issue of sQUlearn which at some point needs to be resolved. For now, the above code works perfectly fine with version 0.10.0.
</issue_description>

Comments on the Issue (you are @copilot in this section)

@MoritzWillmann Thanks for raising @JanSchnabel ! As mentioned this is part of a bigger issue (or actually two issues) in sQUlearn where we somehow misuse attributes/properties and the `set_params` function.

In this particular case LayeredEncodingCircuit has an class property LayeredEncodingCircuit._encoding_circuit_str but no argument encoding_circuit_str in the signature of __init__ or property LayeredEncodingCircuit.encoding_circuit_str (because it's used in the classmethod from_string and thus by convention of the set_params/get_params methods from scikit-learn shouldn't be able to set via set_params (only args from the signature). The failing error we are doing here is not having a property encoding_circuit_str (without setter) so the correct assignment to _encoding_circuit_str

try:
    setattr(self, key, value)
except:
    setattr(self, "_" + key, value)

works as specified in set_params of the EncodingCircuitBase. Since we don't have the property without a setter, assigning the attribute encoding_circuit_str will not fail but won't be used by later calls to the property (expects _encoding_circuit_str). We could fix this by adding the property

@property
def encoding_circuit_str(self):
    return self._encoding_circuit_str

but it would actually be cleaner to not allow _encoding_circuit_str to be set via the set_params function since it's not part of the signature of the class. The issue with the absence of a property (without setter) persists for other attributes and classes throughout sQUlearn. I would thus suggest the following fixes

  • removing all non-signature attributes from get_params
  • adding properties for remaining attributes

Cheers Moritz</comment_new>


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

…arameter_str

Co-authored-by: MoritzWillmann <44642314+MoritzWillmann@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix encoding_circuit_str in set_params() of LayeredEncodingCircuit Fix set_params() for encoding_circuit_str, feature_str, and parameter_str in LayeredEncodingCircuit Feb 17, 2026
Copilot AI requested a review from MoritzWillmann February 17, 2026 13:52
Copilot AI and others added 8 commits February 17, 2026 13:57
…gnature params in LayeredEncodingCircuit and ParamZFeatureMap

Co-authored-by: MoritzWillmann <44642314+MoritzWillmann@users.noreply.github.com>
- ChebyshevRx: Added properties for num_layers, closed, alpha, nonlinearity
- ChebyshevPQC: Added properties for num_layers, closed, entangling_gate, alpha, nonlinearity
- KyriienkoEncodingCircuit: Added properties for num_encoding_layers, num_variational_layers, variational_arrangement, encoding_style, block_width, block_depth, rotation_gate
- Changed instance variables to use underscore prefix (_param) for properties
- Properties placed before get_params() method with proper type hints and docstrings

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…arameters

Co-authored-by: MoritzWillmann <44642314+MoritzWillmann@users.noreply.github.com>
- Changed attribute assignments in __init__ to use underscore prefix
- Added @Property decorators for read-only access to parameters
- Updated all internal references to use underscore-prefixed attributes
- Properties placed before get_params() method
- Follows pattern from HubregtsenEncodingCircuit

Files updated:
- single_probability.py: qubit, one_state, parameterized
- custom_observable.py: operator_string, parameterized
- summed_probabilities.py: one_state, full_sum, include_identity
- ising_hamiltonian.py: I, Z, X, ZZ
- summed_paulis.py: op_str, full_sum, include_identity
- single_pauli.py: qubit, op_str, parameterized

All existing tests pass. set_params() works correctly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add read-only properties to MultiControlEncodingCircuit (num_layers, closed, final_encoding)
- Add read-only properties to ChebyshevTower (num_chebyshev, alpha, num_layers, rotation_gate, hadamard_start, arrangement, nonlinearity)
- Add read-only properties to RandomLayeredEncodingCircuit (seed, min_num_layers, max_num_layers, feature_probability)
- Add read-only properties to HighDimEncodingCircuit (cycling, cycling_type, num_layers, layer_type, entangling_gate)

All properties follow the same pattern as HubregtsenEncodingCircuit:
- Use underscore prefix for internal storage
- Provide read-only access via @Property decorators
- Maintain backward compatibility with get_params/set_params

All existing tests pass successfully.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Added detailed documentation to the num_layers property explaining the
lazy initialization behavior. While the property is read-only from an
external perspective (users cannot directly assign to it), it is
internally mutable to support the lazy initialization pattern where
num_layers=None is auto-determined based on features during get_circuit().

This design is intentional and tested for backward compatibility.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: MoritzWillmann <44642314+MoritzWillmann@users.noreply.github.com>
Copilot AI changed the title Fix set_params() for encoding_circuit_str, feature_str, and parameter_str in LayeredEncodingCircuit Enforce signature-only parameters in get_params/set_params across encoding circuits and observables Feb 17, 2026
@MoritzWillmann
Copy link
Collaborator

@copilot please fix the failed black formatting check

…ayered_encoding_circuit.py

Co-authored-by: MoritzWillmann <44642314+MoritzWillmann@users.noreply.github.com>
Copy link
Author

Copilot AI commented Feb 17, 2026

@copilot please fix the failed black formatting check

Fixed in commit 6733257. Removed trailing whitespace from docstrings in highdim_encoding_circuit.py and test_layered_encoding_circuit.py.

@MoritzWillmann MoritzWillmann marked this pull request as ready for review February 17, 2026 14:51
@MoritzWillmann MoritzWillmann self-requested a review February 17, 2026 15:12
@MoritzWillmann
Copy link
Collaborator

@copilot can you check every class in squlearn that has get_params/set_params (either explicitly implemented or inherited) and adapt to use the same logic

Co-authored-by: MoritzWillmann <44642314+MoritzWillmann@users.noreply.github.com>
Copy link
Author

Copilot AI commented Feb 17, 2026

@copilot can you check every class in squlearn that has get_params/set_params (either explicitly implemented or inherited) and adapt to use the same logic

Checked all classes with get_params/set_params across the entire codebase. Fixed QGPR and QKRR in commit 0a25803.

Summary:

  • Encoding circuits (17 classes): 7 fixed earlier, 4 already had properties, 6 don't override get_params
  • Observables (6 classes): All fixed earlier
  • Kernel classes (5 classes): QGPR and QKRR now fixed, QSVC/QSVR/QGPC use sklearn base classes (already correct)
  • QNN/QRC classes (2 classes): Use super().get_params() pattern (already correct)

All classes now follow the pattern: only signature parameters in get_params(), with read-only properties for parameters that need them.

Copilot AI changed the title Enforce signature-only parameters in get_params/set_params across encoding circuits and observables Enforce signature-only parameters in get_params/set_params across all squlearn classes Feb 17, 2026
Copilot AI requested a review from MoritzWillmann February 17, 2026 15:42
@MoritzWillmann MoritzWillmann merged commit a52cdbe into develop Feb 17, 2026
31 checks passed
@MoritzWillmann MoritzWillmann deleted the copilot/fix-encoding-circuit-str-issue branch February 17, 2026 16:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants