Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiplying polynomials #34

Open
wants to merge 56 commits into
base: master
Choose a base branch
from

Conversation

aliang8
Copy link
Contributor

@aliang8 aliang8 commented May 21, 2017

Added rule for multiplying polynomials.

x^2 * x^1', '1 x^3

Copy link
Member

@kevinbarabash kevinbarabash left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove all changes that are already part #32. Also, MULTIPLY_POLYNOMIALS should be split up.

lib/rules.js Outdated
@@ -80,6 +81,8 @@ export const EVALUATE_POWER = defineRuleString(
// e.g. -(-3) -> 3
export const NEGATION = defineRuleString('--#a', '#a')

export const REARRANGE_COEFF = defineRuleString('#b * #a', '#a #b', {a: query.isNumber, b: isPolynomialTerm})

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't be included b/c it's part of the other diff.

lib/rules.js Outdated
}
}
}
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indentation

lib/rules.js Outdated
@@ -196,6 +235,70 @@ export const PRODUCT_RULE = defineRuleString('#a^#b_0 * ...', '#a^(#b_0 + ...)')
// e.g. x^5 / x^3 -> x^(5 - 3)
export const QUOTIENT_RULE = defineRuleString('#a^#p / #a^#q', '#a^(#p - #q)')

// e.g. x^2 * x^2 -> x^4
export const MULTIPLY_POLYNOMIALS = defineRule(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mathsteps has no MULTIPLY_POLYNOMIALS... it has a set of steps. We should implement the steps in https://github.com/socraticorg/mathsteps/blob/master/lib/ChangeTypes.js#L60-L69 unless there's a strong reason why we shouldn't.

lib/rules.js Outdated
@@ -226,6 +329,39 @@ export const DISTRIBUTE_NEGATIVE_ONE =
// COLLECT AND COMBINE
export {default as COLLECT_LIKE_TERMS} from './rules/collect-like-terms'

export const FRACTIONAL_POLYNOMIALS = defineRule(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is from the other PR as well. It shouldn't be part of this PR.

//['x^2 * x', 'x^2 * x^1'],
//['x^2 * 2 * x * x', ''],
//['x + 2 * x', '']
])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix test cases and uncomment.

Copy link
Member

@kevinbarabash kevinbarabash left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need both ADD_EXPONENT_OF_ONE_HELPER and ADD_EXPONENT_OF_ONE. I feel like ADD_EXPONENT_OF_ONE_HELPER is simpler and should do everything that we need for this rule.

['x^2 * 2 * x * x', ''],
['x + 2 * x', '']
['x^2 * 2 * x * x', 'x^2 * 2 * x^1 * x^1'],
['2x + 3x^2 * x y z', '2 x + 3 x^2 * (x^1 * y^1 * z^1)'],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should maintain the implicit flag on x y z. I would expect the output of this test to be:

2 x^1 + 3 x^2 * x^1 y^1 z^1

The first term should have a ^1 added as well, shouldn't it?

lib/rules.js Outdated
'mul',
node.args.map(arg => {
if (canApplyRule(ADD_EXPONENT_OF_ONE_HELPER, arg)) {
console.log("1st")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove

lib/rules.js Outdated
}
}
})
return arg
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you put this in an else block?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

lib/rules.js Outdated
if (query.isMul(node)) {
node.args.some(arg => {
hasConstantVariable = query.isIdentifier(arg)
})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a confusing construct... the following would be clearer:

const hasConstantVar = query.isMul(node) && node.args.some(query.isIdentifier)

lib/rules.js Outdated
}

return (hasConstantVariable) ?
{node} : null
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For ternaries please put them on a single line or the following:

return hasConstantVariable
    ? {node}
    : null

lib/rules.js Outdated
return build.applyNode('pow', [arg, build.numberNode(1)])
}
return arg
}), {implicit: true}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

{implicit: node.implicit}

@aliang8
Copy link
Contributor Author

aliang8 commented May 23, 2017

The helper rule does it for a single polynomial, specially when they're an implicit multiplication of terms (e.g 6xyz), while the rule itself applies the helper to all the terms and handles x. I think it would be easier with the helper, but if you feel like it is unnecessary , I could try to find another way to combine them.

['x^2 * x^1', '(1 * 1) (x^2 * x^1)'],
['3x^2 * x^2', '(3 * 1) (x^2 * x^2)'],
['x^3 * 2y^2', '(1 * 2) (x^3 * y^2)'],
['x^3 + 2x + 3x^1 * 5x^1', 'x^3 + 2 x + (3 * 5) (x^1 * x^1)'],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice test cases. I was thinking it'd be nice to be able to check similar inputs which fail to meet the rule's criteria. I've added a task for it #36.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a test case involving the product of three polynomials that are the same?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

['3x^2 * x^2', '3 x^4'],
['x^3 * 2y^2', '2 (x^3 y^2)'],
['x^3 + 2x + 3x^1 * 5x^1', 'x^3 + 2 x + 15 x^2'],
])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does a couple of steps:

  • multiplying the coefficients
  • actually doing evaluating the product

Mathsteps still wants all of the steps. I feel like we need to take a step back and think about we can combine multiple steps. We could use MULTIPLY_COEFFICIENTS + SIMPLIFY_ARITHMETIC, but we currently don't have a way to tell SIMPLIFY_ARITHMETIC that we want it to simplify only the nodes that MULTIPLY_COEFFICIENTS manipulated.

lib/rules.js Outdated
// e.g. x^2 * x -> x^2 * x^1
// export const ADD_EXPONENT_OF_ONE = ...
export const ADD_EXPONENT_OF_ONE = defineRule(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This rule could be implemented without the helper once we address #23. Can you add a TODO here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

lib/rules.js Outdated
if (query.isIdentifier(arg)){
return build.applyNode('pow', [arg, build.numberNode(1)])
}
return arg
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you use an else block here or change this to ternary statement?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

lib/rules.js Outdated
const {constants, coefficientMap} = getCoefficientsAndConstants(node)
isMulOfPolynomials = Object.keys(coefficientMap).length > 1
|| Object.keys(coefficientMap)
.some(key => coefficientMap[key].length > 1)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is kind of confusing. The coefficientMap is designed for collecting like terms, but makes this task more complicated. Instead try using getVariableFactors.

Copy link
Contributor Author

@aliang8 aliang8 May 28, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getVariableFactors doesn't really help in the case of :
2x + 3x^2 * x y z => 2 x + 3 x^2 * x^1 y^1 z^1

It returns []

lib/rules.js Outdated
// e.g. 3x^2 * 2x^2 -> (3 * 2)(x^2 * x^2)
export const MULTIPLY_COEFFICIENTS = defineRule(
(node) => {
return canApplyRule(ADD_EXPONENT_OF_ONE, node) ? {node} : null
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't ADD_EXPONENT_OF_ONE only be matching things like x or x * x? This matching function though seems to be accepting x^2 which I would expect ADD_EXPONENT_OF_ONE to ignore.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess ADD_EXPONENT_OF_ONE_HELPER does what you want. ADD_EXPONENT_OF_ONE looks through the given expression and applies the helper where necessary.

@@ -380,6 +386,31 @@ describe('rules', () => {
['x^-a / x^-b', 'x^(-a - -b)'],
])

suite('multiplying coefficients', rules.MULTIPLY_COEFFICIENTS, [
['x^2 * y^2', '(1 * 1) (x^2 * y^2)'],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels like a really result. We end up with a more complicated expression that actually contains the original expression. Maybe check with Gen to see if we actually want this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at the tests in collectAndCombineSearch.test.js mathsteps doesn't bother showing the 1 coefficients.

    ['x^2 * x * x',
      ['x^2 * x^1 * x^1',
        'x^(2 + 1 + 1)',
        'x^4'],
    ],
    ['y * y^3',
      ['y^1 * y^3',
        'y^(1 + 3)',
        'y^4'],
    ],
    ['2x * x^2 * 5x',
      ['(2 * 5) * (x * x^2 * x)',
        '10 * (x * x^2 * x)',
        '10x^4'],
      '10x^4'
    ],

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm okay with deferring the work to get this behavior until we move this code over to mathsteps. Can you add a TODO so that we can remind ourselves of this difference in behavior?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed weird behavior in new commit.

['x^2y^2z^2 * 2x^2', '(1 * 2) (x^2 * y^2 * z^2 * x^2)'],
['3x^2 * x^2', '(3 * 1) (x^2 * x^2)'],
['x^3 * y^2', '(1 * 1) (x^3 * y^2)'],
['x^2 * y^2', 'x^2 * y^2'],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might need tweak when it gets moved over to mathsteps b/c this should not be a change step b/c nothing changed but right now as long as rewriteNode returns a non-null value we assume that a change has occurred. We can take care of tweaking like that in mathsteps though.

? build.implicitMul(build.mul(...coeffs), poly)
: coeffs.length == 1
? build.implicitMul(coeffs[0], poly)
: poly
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please no nested ternaries. They're hard to understand and even harder understand with this formatting.

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.

2 participants