Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitde5d6e5

Browse files
committed
fix/refactor: disregard symbols less-so for 'canonicalPower'
jointly, do more checks on operand types (particularly for functionexprs.), more strictly check for type 'number'; less eager insimplifying FN-expressions as operands.
1 parenta25459b commitde5d6e5

File tree

2 files changed

+129
-37
lines changed

2 files changed

+129
-37
lines changed

‎src/compute-engine/boxed-expression/arithmetic-power.ts‎

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -68,17 +68,13 @@ export function canonicalPower(
6868
// here)
6969
if(
7070
b.isFunctionExpression||
71-
!b.isConstant||//(!notably, should exclude symbol assumptions)
71+
b.symbol!==null||
7272
!b.type.matches('number'asType)
7373
)
7474
returnunchanged();
7575

76-
// 'a' is constant and of type 'number'. Used often.
77-
// (note that this could still be a numeric expression, too...)
78-
constaIsNum=a.isConstant&&a.type.matches('number'asNumericType);
79-
8076
// Zero as base
81-
if(aIsNum&&a.is(0)){
77+
if(a.isNumberLiteral&&a.is(0)){
8278
if(b.type.matches('imaginary'asNumericType)||b.isNaN)returnce.NaN;
8379

8480
if(b.is(0))returnce.NaN;
@@ -97,9 +93,19 @@ export function canonicalPower(
9793
returnunchanged();// No other canonicalization cases with this base
9894
}
9995

96+
// 'a'/base has an associated number value (excludes numeric functions)
97+
// (this should at this stage include library-defined symbols such as 'Pi')
98+
//@note: include 'Negate', because this could be wrapped around a number-valued symbol, such as
99+
//'Pi'...
100+
//^there could exist other exceptions: perhaps consider a util. such as 'maybeNumber'?
101+
constaIsNum=
102+
a.type.matches('number'asNumericType)&&
103+
(a.isFunctionExpression===false||a.operator==='Negate');
104+
100105
// Zero as exponent
101106
if(b.is(0)){
102-
if(aIsNum)returna.isFinite ?ce.One :ce.NaN;
107+
// If 'isFinite' is a boolean, then 'a' has a value.
108+
if(aIsNum&&a.isFinite!==undefined)returna.isFinite ?ce.One :ce.NaN;
103109
returnunchanged();
104110
}
105111

@@ -109,7 +115,8 @@ export function canonicalPower(
109115
if(aIsNum&&a.is(1))returnb.isFinite ?ce.One :ce.NaN;
110116

111117
// One as exponent
112-
if(b.is(1))returna;
118+
// (Permit the base to be a FN-expr. here, too...)
119+
if(b.is(1)&&a.type.matches('number'asNumericType))returna;
113120

114121
// -1 exponent
115122
if(b.is(-1)){
@@ -176,8 +183,8 @@ export function canonicalPower(
176183
returnce.NaN;
177184
}
178185

179-
//'Infinity^Complex'
180-
if(a.isInfinity){
186+
//'AnyInfinity^{~oo}' (i.e. ComplexInfinity)
187+
if(a.isNumberLiteral&&a.isInfinity){
181188
// If the exponent is pure imaginary, the result is NaN
182189
//(↓fix?:ensure both these cases narrow down to 'b' being a num./symbol literal)
183190
if(b.type.matches('imaginary'asNumericType))returnce.NaN;

‎test/compute-engine/canonical-form.test.ts‎

Lines changed: 112 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -240,12 +240,38 @@ describe('CANONICAL FORMS', () => {
240240

241241
describe('Power',()=>{
242242
constengine=newComputeEngine();
243-
engine.declare('x',{constant:true,value:0});
244-
engine.declare('y',{constant:true,value:1});
245243

246-
engine.declare('p',{constant:true,value:Infinity});
247-
engine.declare('n',{constant:true,value:-Infinity});
248-
engine.declare('c',{constant:true,value:-Infinity});
244+
/*
245+
* 'Power'-scoped symbol declarations
246+
*/
247+
//@note: declarations are necessarily marked with 'holdUntil: never' - for these test cases - in
248+
//order to permit substitution during (before) application of canonical-forms.
249+
//^Necessary because 'a^x' is not a valid simplification (cannot assume that 'x' is bound), but
250+
//if x is substituted, then that's fine...
251+
engine.declare('x',{constant:true,value:0,holdUntil:'never'});
252+
engine.declare('y',{constant:true,value:1,holdUntil:'never'});
253+
254+
engine.declare('p',{
255+
constant:true,
256+
value:Infinity,
257+
holdUntil:'never',
258+
});
259+
engine.declare('n',{
260+
constant:true,
261+
value:-Infinity,
262+
holdUntil:'never',
263+
});
264+
engine.declare('c',{
265+
constant:true,
266+
value:-Infinity,
267+
holdUntil:'never',
268+
});
269+
270+
//Control-case symbols (holdUntil value of default 'evaluate', and henceforth 'value' not to be
271+
//consulted at this stage)
272+
engine.declare('j',{constant:true,value:0});
273+
engine.declare('k',{constant:true,value:1});
274+
249275
constcheckPower=(
250276
input:string,
251277
priorForms:CanonicalForm[]=['Number']
@@ -267,12 +293,21 @@ describe('CANONICAL FORMS', () => {
267293
canonForms = ComplexInfinity
268294
canonical = ComplexInfinity
269295
`);
270-
// x === 0
296+
// x === 0 (simplifies because is substituted beforehand ('holdUntil = never'))
271297
expect(checkPower('x^3')).toMatchInlineSnapshot(`
272298
box = ["Power", "x", 3]
273299
canonForms = 0
274300
canonical = 0
275301
`);
302+
303+
// Control
304+
//
305+
//'j=0', but should _not_ substitute (i.e. beforehand) & therefore no op. should take place
306+
//(its 'holdUntil' property is set to the default 'evaluate')
307+
expect(checkPower('j^3')).toMatchInlineSnapshot(`
308+
box = ["Power", "j", 3]
309+
canonForms = ["Power", "j", 3]
310+
`);
276311
});
277312

278313
test('x^0',()=>{
@@ -286,11 +321,18 @@ describe('CANONICAL FORMS', () => {
286321
canonForms = NaN
287322
canonical = NaN
288323
`);
324+
//↓Only simplifies because 'x' substitutes with its value during partial-canonicalization;
325+
//prior to app. of CanonicalForms.
289326
expect(checkPower('16^x')).toMatchInlineSnapshot(`
290327
box = ["Power", 16, "x"]
291328
canonForms = 1
292329
canonical = 1
293330
`);
331+
//!@note: this should be ok to apply, despite 'Pi' being a constant which does *not* have a
332+
//!'holdUntil' value set to 'never', because... this is a library/engine-level defined
333+
//!constant in which its type/value is always known: even in non-canonical expressions.
334+
//I.e., if this were a user-defined constant, this would not be OK, since its value would have
335+
//to be referenced.
294336
expect(checkPower('\\pi^0')).toMatchInlineSnapshot(`
295337
box = ["Power", "Pi", 0]
296338
canonForms = 1
@@ -301,6 +343,15 @@ describe('CANONICAL FORMS', () => {
301343
canonForms = 1
302344
canonical = 1
303345
`);
346+
347+
/*
348+
* Control-case
349+
*/
350+
//j===0, but is *held*
351+
expect(checkPower('2^j')).toMatchInlineSnapshot(`
352+
box = ["Power", 2, "j"]
353+
canonForms = ["Power", 2, "j"]
354+
`);
304355
});
305356

306357
test('1^x',()=>{
@@ -342,15 +393,41 @@ describe('CANONICAL FORMS', () => {
342393
canonForms = PositiveInfinity
343394
canonical = PositiveInfinity
344395
`);
345-
expect(checkPower('{-a/4}^1')).toMatchInlineSnapshot(`
346-
box = ["Power", ["Divide", ["Negate", "a"], 4], 1]
347-
canonForms = ["Divide", ["Negate", "a"], 4]
348-
canonical = ["Multiply", ["Rational", -1, 4], "a"]
349-
`);
350-
expect(checkPower('{-\\pi}^1')).toMatchInlineSnapshot(`
351-
box = ["Power", ["Negate", "Pi"], 1]
352-
canonForms = ["Negate", "Pi"]
353-
canonical = ["Negate", "Pi"]
396+
397+
//@note: This correctly simplifies, unlike would be expected of 'x^1' (where x is a
398+
//user-declared numeric constant with a default 'holdUntil' value of 'evaluate'), because 'Pi'
399+
//is a library-defined constant, consequently always being canonical & bound (and ∴ having a
400+
//type/value), even in *non-canonical* exprs.
401+
//^!Note that currently, 'x^1' *will simplify* at present, but this would not be considered
402+
//correct behaviour (compute-engine/pull/238#discussion_r2034335185). This is because
403+
//canonicalization of symbols (including partial-canonicalization) is presently, erroneously,
404+
//synonymous with 'binding': permitting its type/value to be determined at this stage.
405+
expect(checkPower('{\\pi}^1')).toMatchInlineSnapshot(`
406+
box = ["Power", "Pi", 1]
407+
canonForms = Pi
408+
canonical = Pi
409+
`);
410+
411+
//↓🙁: cannot be simplified, at least via the route of checking the 'type' of the base/'-\pi',
412+
//because is a non-canonical function-expr. (non-bound to 'Power' definition)
413+
414+
// expect(checkPower('{-\\pi}^1')).toMatchInlineSnapshot(`
415+
// box = ["Power", "Pi", 1]
416+
// canonForms = Pi
417+
// canonical = Pi
418+
// `);
419+
420+
/*
421+
* Control
422+
*/
423+
//!@note:
424+
//Not currently simplified, despite 'j' being an 'integer'-typed constant, because the base is
425+
//a non-canonical function and therefore unbound to a definition; consequently with its 'type'
426+
//non-determinable.
427+
expect(checkPower('{-j/4}^1')).toMatchInlineSnapshot(`
428+
box = ["Power", ["Divide", ["Negate", "j"], 4], 1]
429+
canonForms = ["Power", ["Divide", ["Negate", "j"], 4], 1]
430+
canonical = ["Multiply", ["Rational", -1, 4], "j"]
354431
`);
355432
});
356433

@@ -377,10 +454,10 @@ describe('CANONICAL FORMS', () => {
377454
canonForms = ["Rational", 1, 3]
378455
canonical = ["Rational", 1, 3]
379456
`);
380-
expect(checkPower('{x * 4}^{-1}')).toMatchInlineSnapshot(`
381-
box = ["Power", ["Multiply", "x", 4], -1]
382-
canonForms = ["Divide", 1, ["Multiply", 4, "x"]]
383-
canonical = ["Divide", 1, ["Multiply", 4, "x"]]
457+
expect(checkPower('{j * 4}^{-1}')).toMatchInlineSnapshot(`
458+
box = ["Power", ["Multiply", "j", 4], -1]
459+
canonForms = ["Divide", 1, ["Multiply", 4, "j"]]
460+
canonical = ["Divide", 1, ["Multiply", 4, "j"]]
384461
`);
385462
});
386463

@@ -426,7 +503,10 @@ describe('CANONICAL FORMS', () => {
426503
canonical = ComplexInfinity
427504
`);
428505

429-
//Constant-symbol value cases.: p=PositiveInfinity
506+
/*
507+
* Constant-symbol value cases.
508+
*/
509+
// p === +Infinity (& 'holdUntil: never')
430510
expect(checkPower('1^{p}')).toMatchInlineSnapshot(`
431511
box = ["Power", 1, "p"]
432512
canonForms = NaN
@@ -470,13 +550,17 @@ describe('CANONICAL FORMS', () => {
470550
canonForms = 0
471551
canonical = 0
472552
`);
473-
expect(checkPower('{-\\pi}^{-\\infty}')).toMatchInlineSnapshot(`
474-
box = ["Power", ["Negate", "Pi"], "NegativeInfinity"]
475-
canonForms = ["Power", ["Negate", "Pi"], "NegativeInfinity"]
476-
canonical = 0
477-
`);
478-
479-
//Constant-symbol value cases.: n=NegativeInfinity, x=0
553+
//🙁: cannot be simplified, base/'Negate' is non-canonical and 'type' cannot be determined.
554+
// expect(checkPower('{-\\pi}^{-\\infty}')).toMatchInlineSnapshot(`
555+
// box = ["Power", ["Negate", "Pi"], "NegativeInfinity"]
556+
// canonForms = ["Power", ["Negate", "Pi"], "NegativeInfinity"]
557+
// canonical = 0
558+
// `);
559+
560+
/*
561+
* Constant-valued symbol operands
562+
*/
563+
//x=0, n=NegativeInfinity ('holdUntil: never'),
480564
expect(checkPower('x^{n}')).toMatchInlineSnapshot(`
481565
box = ["Power", "x", "n"]
482566
canonForms = ComplexInfinity
@@ -541,6 +625,7 @@ describe('CANONICAL FORMS', () => {
541625
`);
542626

543627
//Include 'Add' in order that complex-numbers may be identified for these cases.
628+
//(^note that this may later be included as part of the 'Number' form)
544629
check=(input:string)=>
545630
checkPower(input,['InvisibleOperator','Number','Add']);
546631

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp