Add user-defined domain support to Minterpy polynomials#81
Merged
Conversation
Add Domain class to handle affine coordinate transformations between user-defined rectangular domains and the normalized domain [-1,1]^m. Features: - Affine transformations: `map_to_normalized()`, `map_from_normalized()` - Factory methods: `Domain.uniform()`, `Domain.normalized()` - Scaling factors: `get_diff_factor()` (chain rule), `get_int_factor()` (Jacobian) - Domain utilities: `contains()`, `partial_matching()`, `is_normalized` - Optional validation with validate parameter Additional implementation notes: - Uses "normalized" terminology instead of "canonical" to avoid confusion - Comprehensive test suite with 35+ tests across 9 test classes Also adds: - verify_domain_bounds() utility function for input validation - `InvalidDomainBoundsError` as a new custom exception derived from `ValueError` This commit should resolve Issue #51
Implement Domain class for rectangular domain transformations
Implement `__or__` dunder method to combine domains with different dimensions via the | operator. Add expand_dim() method to support dimension expansion with either integer or Domain targets. - The implementation of __or__ is delegated to expand_dim() - Add expand_dim(int) for expanding normalized domains - Add expand_dim(Domain) for expanding to match target domain - Raise (a new) DomainMismatchError when domains not partially matched - Raise ValueError for invalid dimension targets - Add comprehensive test coverage for both operations This commit should resolve Issue #62.
Add union operator and expand_dim method to Domain class
The Domain class has been integrated as a property of the Grid class for dealing with custom rectangular domains while maintaining backward compatibility with normalized [-1, 1]^m domain. Several key changes: - Add optional `domain` parameter to Grid constructor and all factory methods - Domain defaults to normalized `Domain.normalized(m)` when not specified - Add `domain` property - Update `__call__` to transform unisolvent_nodes to user domain before evaluation - Add domain equality check to `__eq__` taking into account the domain - Update `__mul__` and `__or__` to combine domains via domain union (|) The test suite has been updated accordingly. This commit should resolve Issue #52.
Integrate Domain class with Grid
Add tests on taking the union and product of two instances of Grid with different domains should raise DomainMismatchError. This commit is part of the resolution of Issue #52.
Update tests for Grid for DomainMismatchError
An instance of `Domain` can now be passed as a construction parameter of a polynomial with the following validation logic: - When both `grid` and `domain` are None: construct default grid with default domain - When only `grid` specified: use grid's existing domain - When only `domain` specified: construct grid with specified domain - When both specified: validate consistency, raise `DomainMismatchError` if mismatch Furthermore, the `Domain` instance associated with a grid is a read-only property of all polynomial instances. While the particular property is not used in any underlying logic, this addition establishes the foundation for domain-aware polynomial operations by making domain accessible as a first-class property while maintaining backward compatibility. This commit should resolve Issue #66.
Add domain to the Polynomial abstract class
- Added `is_uniform` property for checking domain uniformity. - Enhanced `is_normalized` with tolerance-based checks (`DEFAULT_RTOL`, `DEFAULT_ATOL`). - Enhanced `expand_dim` to handle identity and uniform domains. - Overhauled `__eq__` and `partial_matching` for stricter or more flexible comparisons, respectively. - Improved test coverage for union and dimension expansion edge cases. - Updated private `_bounds` attribute and introduced `bounds` as a property.
Domain verification for polynomial operations now happens implicitly through grid operations rather than explicit pre-checks. When adding or multiplying polynomials, domain compatibility is validated by the underlying grid union/product operations, which raise `DomainMismatchError` if domains are incompatible. Key changes: Removed explicit domain checks: - Polynomial addition and multiplication no longer pre-verify domain matching before delegating to grid operations (union and product) - Domain incompatibility is now caught by grid operations themselves Simplified dimension handling: - Polynomial-polynomial operations no longer assume matching dimensions before delegation to concrete implementations - Dimension matching is now handled at the lower level when required - `expand_dim()` method simplified to only support expansion to a target dimension (integer) Deprecations: - `has_matching_dimension()` method (polynomial abstract class) - `has_matching_domain()` method (polynomial abstract class) - `_match_dims()` private method (polynomial abstract class) - `verify_poly_domain()` verification function (removed prior to deprecating `user_domain` and `internal_domain`) New features: - `__call__()` method now accepts optional keyword-only parameter `truncate_cols`: allows lower-dimensional polynomials to be evaluated on higher-dimensional points All tests have been updated to reflect these changes. This commit should resolve Issues #67 and #68.
Refactor domain handling in Grid and Polynomial operations
The logic has been improved for clarity in checking the grid compatibility. The corresponding test case has also been improved.
Introduced internal bounds to the `Domain` class to lay the groundwork for deprecating the notion of `internal_domain` directly attributed to polynomial instances. Internal bounds are currently hard-coded to `[-1, 1]^m` such that there is no change in behavior. However, the infrastructure is now in place for future generalization where a single Domain instance manages both user and internal domain. Changes: - Renamed public properties for brevity and clarity: `lower_bounds` to `lower`, `upper_bounds` to `upper`, `domain_widths` to `widths` - Added `internal_bounds` property (public, lazy evaluation) - Add private properties: `_internal_lowers`, `_internal_uppers`, `_internal_widths` - Renamed `map_to_normalized()` to `map_to_internal()` - Renamed `map_from_normalized()` to `map_from_internal()` - Update `map_to_internal()` and `map_from_internal()` to use internal bounds instead of hard-coded [-1, 1] - Update `is_normalized` to check against internal bounds with numerical tolerance - Add `internal` parameter to `contains()` for validating points againsts internal domain - Update `get_int_factor()` and `get_diff_factor()` to compute scaling factors relative to internal bounds - Upate `__eq__()` to check both user bounds and internal bounds (this remains a strict equality) - Update `partial_matching()` to take into account internal domain (this remains with tolerance) - Update tests to cover new functionality
Simplified generating points validation by leveraging the `Domain` class's internal bound checking. This removes `check_domain_fit()` verification function and consolidate all validation logics into the private method `_verify_generating_points()` of the `Grid` class. Changes: - Remove `check_domain_fit()` verification function (the core functionality is now replaced by `domain.contains(..., internal=True)` - Refactor `_verify_generating_points()` method in the `Grid` class to handle generating points validation. - Change the method `_verify_generating_points()` to return the validated generating points to be consistent with "validate-and-assign" pattern. - Update the tests to reflect the changes
The concepts of user domain and internal domain have been superseded by the `Domain` class integration. These legacy properties are no longer needed as domain information is now contained in the instance of the new class (attached to the instance of `Grid`). Changes: - Remove `internal_domain` and `user_domain` parameters from `__init__()` - Remove abstract methods `generate_internal_domain()` and `generate_user_domain()` - Remove `internal_domain` and `user_domain` from all factory methods: - `from_degree()` - `from_poly()` - `from_grid()` - Refactor tests to validate `Domain` functionality and remove outdated domain-dependent logic. - Clean up unused imports and deprecated methods like `verify_domain()`. Breaking changes: - Constructor for polynomials no longer accepts `internal_domain` or `user_domain` parameters This commit resolves Issue #69 and partially resolves Issue #53.
Integrate Domain class and remove legacy domain properties
Add an identity check to short-circuit equality comparison when instances of `MultiIndexSet` are identical.
Separate arithmetic operations into dedicated modules: - `mp.polynomials.arithmetic.py`: Core coefficient computation logic - `mp.polynomials.scalar_add.py`: Scalar addition for canonical, Newton, and Chebyshev bases Additonal changes: - Minimize repetitions in the concrete implementations - Remove redundant implementation in `mp.utils.polynomials.interface` This commit resolves Issue #70 and partially resolves Issue #53.
Refactor polynomial arithmetic into modular components
Add all changes related to the resolution of Issue #53 in the Unreleased section.
Integrate Domain and (Domain-aware) Grid into Polynomial Classes
Query points passed to polynomial `__call__` are now expected in the polynomial's user domain (default: `[-1, 1]^m`). Points are automatically transformed to the internal domain before evaluation, eliminating manual coordinate transformations. The internal polynomial representation remains unchanged and continues to use the internal domain (currently `[-1, 1]^m`). Key changes: - Refactor `__call__` to accept user domain points with automatic transformation - Add `eval_on_internal()` method for direct internal evaluation bypassing transformation - Rename `Domain.is_normalized` to `Domain.is_identity` for clarity - Refactor the verification function `verify_query_points()` to `standardize_query_points()` with integrated column truncation - Update polynomial evaluation tests to verify domain invariance This commit resolves Issue #54.
Add domain-aware polynomial evaluation
Polynomial integration now supports custom rectangular domains, automatically applying Jacobian scaling factors to account for domain transformations. The changes is fully backward compatible; default domain remains in `[-1, 1]^m`. Key changes: - Enable integration over user-defined domains with automatic Jacobian scaling - Remove strict bounds validation: integration outside domain bounds is allowed (extrapolation) but should be used with care - Refactor `Domain` class: rename `get_int_factor()` to `int_factor()`; `is_identity` property is now lazily evaluated and cached - Centralize integration tests into a dedicated test module This commit resolves Issue #56.
Add domain-aware polynomial integration
The method `get_diff_factor()` in the `Domain` class has been renamed to `diff_factor()` for brevity and consistency with `int_factor()`. Key changes: - Rename `get_diff_factor()` to `diff_factor()` - Short-circuit zero-order differentiation, i.e., immediately returns a factor of 1.0 without any computation - Update the relevant tests Ref: #55
Polynomial differentiation now correctly takes into account rectangular user domains by scaling the polynomial with the correct differentiation factor (a function of the given order of derivatives). Key changes: - Use `diff_factor()` from the `Domain` class to scale differentiated polynomials according to the user domain and derivative order - Remove the static abstract method `_partial_diff()` from the polynomial abstract base class in favor of centralizing the operation to `diff()` - Refactor `partial_diff()` to be syntactic sugar for `diff()` - Remove the static concrete method `_partial_diff()` from `NewtonPolynomial` and `CanonicalPolynomial` - Implement zero-order differentiation as identity operation (returns a copy of the polynomial) - Expand the test suite for polynomial differentiation, focusing on mathematical properties - Centralize polynomial differentiation tests into a dedicated test module Refs: #50 Resolves: #55
The high-level function `interpolate()` and classes `Interpolator` and `Interpolant` now support custom rectangular domains via the `bounds` parameter. Users can specify domain bounds directly without manually creating `Domain` instances. The underlying interpolating polynomials are automatically constructed with domain awareness. Key changes: - Add `bounds` parameter to `interpolate()`, `Interpolant.from_degree()`, and `Interpolator()` to specify custom rectangular domains - Modernize the syntax of `attrs` (e.g., `attr.ib()`) - Update all tests to cover both default and custom domain cases Ref: #50 Resolves: #57
Add domain support to interpolation module
All occurences of the factory method `Domain.normalized()` have been replaced with `Domain.identity()` to improve naming consistency and better reflect its role as the default internal reference domain.
The Quickstart guide in `README.md` has been updated with the example on a custom domain to demonstrate the new domain-aware capabilities. Additional changes: - Revise the opening paragraphs in `README.md` and the documentation landing page - Revise the installation instructions for clarity - Add citation information for recent publications - Add updated plots Ref: #58
The Quickstart Guide has been updated to include a two-dimensional example on a non-default domain, introducing the `bounds` argument of the `interpolate` function. Ref: #58
The "Arithmetic Operations with Polynomials" tutorial has been updated removing redundant phrasing and addressing minor prose inconsistencies. Furthermore, notes on domain compatibility for polynomial-polynomial operations have been added. Ref: #58
The "1D Polynomial interpolation" tutorial has been updated updated with better explanations, minor inconsistency fixes, and notes regarding the default domain. Ref: #58
The tutorial on multidimensional polynomial interpolation has been revised by refining explanations, adding domain references, and updating the Franke function to align with the custom rectangular domain `[0, 1]^2`. Ref: #58
The tutorial on calculus operations with polynomials has been updated to use the Franke function on its natural domain [0, 1]^2 and to demonstrate the domain feature of Minterpy polynomials. The examples, code, and descriptions have been updated accordingly. Ref: #58
The How-To Guides on `Grid` construction have been updated to introduce the `domain` optional parameter and the default behavior. Ref: #58
The How-To Guide on calling a `Grid` instance has been updated with a custom domain. Ref: #58
The API reference section of the documentation has been updated with the section on `Domain`. Ref: #58
A new page in the fundamental section has been introduced to explain the transformation between user-defined and internal reference domains from a theoretical perspective. Ref: #58
A series of How-To Guides have been added to demonstrate various features of the `Domain` class, including creating an instance with various factory methods, checking instance equality and partial matching, taking the union of instances, and computing scaling factors for differentiation and integration. Ref: #58
Update documentation for the domain feature
Contributor
|
Documentation preview (without executed notebooks) is available. |
Contributor
|
Documentation preview (without executed notebooks) is available. |
The DOI badge provided by RODARE seems not to be rendered correctly. The one from shields.io is used instead for now; the link still goes to RODARE.
432ef0a to
7335347
Compare
Contributor
|
Documentation preview (without executed notebooks) is available. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Introduces a$\Omega = [a_1, b_1] \times \cdots \times [a_m, b_m]$ without manually handling coordinate transformations.
Domainclass and integrates it throughout the Minterpy codebase, allowing users to work directly with functions on custom rectangular domainsMotivation
Previously, users interpolating functions on non-canonical domains had to manually transform unisolvent nodes before function evaluation, transform query points before polynomial evaluation, and track and apply chain rule / Jacobian factors for differentiation and integration. This was error-prone and cumbersome. The new
Domainclass encapsulates all of this transparently.Changes
New
Domainclass (minterpy/core/domain.py): handles affine coordinate transformations between user domains and the internal reference domainDomain.uniform()(uniform domain) andDomain.identity()(default domain, i.e., user == internal thus "identity"), coordinate mapsmap_to_internal()/map_from_internal(), scaling factor helpersget_diff_factor()/get_int_factor(),contains()for domain validation, and theis_identityproperty to skip unnecessary transformationsGrid:domainparameter in constructor (defaults to normalized domain);grid(callable)now evaluates at nodes in the user domain; grid operations (*,|) validate domain consistency__call__()accepts query points in the user domain and transforms them automaticallydiff()andpartial_diff()apply chain rule scaling factors automaticallyintegrate_over()applies Jacobian scaling factors automaticallyboundsparameter ininterpolate(),Interpolator, andInterpolantfor specifying custom domains without manually constructing aDomainobjectDomainMismatchErrorexception for mismatched domain operationsChanged
partial_diff()is now syntactic sugar fordiff(), removing duplicate_partial_diff()static methodsDomainclass instead ofcheck_domain_fit()Interpolatorrefactored to useattrs.definesyntaxRemoved
check_domain_fit()utility functioninternal_domainanduser_domainproperties from polynomial abstract classintegrate_over()(extrapolation now allowed with care)minterpy.utils.polynomials.interfacemoduleBackward Compatibility
Fully backward compatible. When no domain is specified,
Domain.identity(m)is used internally and all behavior is identical to the previous implementation. Theis_identityproperty allows skipping transformations for zero overhead in the default domain case.Resolves: #50