Skip to content

Commit 1d39356

Browse files
committed
ENH: reimplement bijection checks from 10bf4cd
1 parent d5c4eab commit 1d39356

File tree

1 file changed

+88
-4
lines changed

1 file changed

+88
-4
lines changed

rocketpy/Function.py

Lines changed: 88 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2411,20 +2411,99 @@ def integralFunction(self, lower=None, upper=None, datapoints=100):
24112411
outputs=[o + " Integral" for o in self.__outputs__],
24122412
)
24132413

2414+
def isBijective(self):
2415+
"""Checks whether the Function is bijective. Only applicable to
2416+
Functions whose source is a list of points, raises an error otherwise.
2417+
2418+
Returns
2419+
-------
2420+
result : bool
2421+
True if the Function is bijective, False otherwise.
2422+
"""
2423+
if isinstance(self.source, np.ndarray):
2424+
xDataDistinct = set(self.xArray)
2425+
yDataDistinct = set(self.yArray)
2426+
distinctMap = set(zip(xDataDistinct, yDataDistinct))
2427+
return len(distinctMap) == len(xDataDistinct) == len(yDataDistinct)
2428+
else:
2429+
raise TypeError(
2430+
"Only Functions whose source is a list of points can be "
2431+
"checked for bijectivity."
2432+
)
2433+
2434+
def isStrictlyBijective(self):
2435+
"""Checks whether the Function is "strictly" bijective.
2436+
Only applicable to Functions whose source is a list of points,
2437+
raises an error otherwise.
2438+
2439+
Notes
2440+
-----
2441+
By "strictly" bijective, this implementation considers the
2442+
list-of-points-defined Function bijective between each consecutive pair
2443+
of points. Therefore, the Function may be flagged as not bijective even
2444+
if the mapping between the set of points which define the Function is
2445+
bijective.
2446+
2447+
Returns
2448+
-------
2449+
result : bool
2450+
True if the Function is "strictly" bijective, False otherwise.
2451+
2452+
Examples
2453+
--------
2454+
>>> f = Function([[0, 0], [1, 1], [2, 4]])
2455+
>>> f.isBijective()
2456+
True
2457+
>>> f.isStrictlyBijective()
2458+
True
2459+
2460+
>>> f = Function([[-1, 1], [0, 0], [1, 1], [2, 4]])
2461+
>>> f.isBijective()
2462+
False
2463+
>>> f.isStrictlyBijective()
2464+
False
2465+
2466+
A Function which is not "strictly" bijective, but is bijective, can be
2467+
constructed as x^2 defined at -1, 0 and 2.
2468+
2469+
>>> f = Function([[-1, 1], [0, 0], [2, 4]])
2470+
>>> f.isBijective()
2471+
True
2472+
>>> f.isStrictlyBijective()
2473+
False
2474+
"""
2475+
if isinstance(self.source, np.ndarray):
2476+
# Assuming domain is sorted, range must also be
2477+
yData = self.yArray
2478+
# Both ascending and descending order means Function is bijective
2479+
yDataDiff = np.diff(yData)
2480+
return np.all(yDataDiff >= 0) or np.all(yDataDiff <= 0)
2481+
else:
2482+
raise TypeError(
2483+
"Only Functions whose source is a list of points can be "
2484+
"checked for bijectivity."
2485+
)
2486+
24142487
def inverseFunction(self, approxFunc=None, tol=1e-4):
24152488
"""
24162489
Returns the inverse of the Function. The inverse function of F is a
24172490
function that undoes the operation of F. The inverse of F exists if
24182491
and only if F is bijective. Makes the domain the range and the range
24192492
the domain.
24202493
2494+
If the Function is given by a list of points, its bijectivity is
2495+
checked and an error is raised if it is not bijective.
2496+
If the Function is given by a function, its bijection is not
2497+
checked and may lead to innacuracies outside of its bijective region.
2498+
24212499
Parameters
24222500
----------
24232501
approxFunc : callable, optional
24242502
A function that approximates the inverse of the Function. This
24252503
function is used to find the starting guesses for the inverse
24262504
root finding algorithm. This is better used when the inverse
2427-
in complex but has a simple approximation.
2505+
in complex but has a simple approximation or when the root
2506+
finding algorithm performs poorly due to default start point.
24282507
The default is None in which case the starting point is zero.
24292508
24302509
tol : float, optional
@@ -2437,10 +2516,15 @@ def inverseFunction(self, approxFunc=None, tol=1e-4):
24372516
A Function whose domain and range have been inverted.
24382517
"""
24392518
if isinstance(self.source, np.ndarray):
2440-
# Swap the columns
2441-
source = np.flip(self.source, axis=1)
2519+
if self.isStrictlyBijective():
2520+
# Swap the columns
2521+
source = np.flip(self.source, axis=1)
2522+
else:
2523+
raise ValueError(
2524+
"Function is not bijective, so it does not have an inverse."
2525+
)
24422526
else:
2443-
if approxFunc:
2527+
if approxFunc is not None:
24442528
source = lambda x: self.findInput(x, start=approxFunc(x), tol=tol)
24452529
else:
24462530
source = lambda x: self.findInput(x, start=0, tol=tol)

0 commit comments

Comments
 (0)