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

Add new contextUnits.pl implementing units as true MathObject classes #1107

Open
wants to merge 28 commits into
base: develop
Choose a base branch
from

Conversation

dpvc
Copy link
Member

@dpvc dpvc commented Aug 29, 2024

This PR adds the file contextUnits.pl to the macros/contexts directory. It implements units as true MathObject classes, so that you can use units and numbers with units as you would any other MathObject. With this context, you can do

$n = Compute("3 m/sec");

rather than having to use NumberWithUnit() to create them.

It also means you can do things like

$n = Compute("3m + 2cm");             # equivalent to Compute("3.02m")
$v = Compute("50 miles / (2 hours)"); # equivalent to Compute("25 mi/h")
$l = Compute("3m, 4cm, 2sec");        # a list of numbers with units

or even

$v = Compute("50 miles per hour");
$a = Compute("9.8 meters per second squared");

Since units are now MathObjects, even without a number, you can use

$u = Compute("m/s^2");

and ask a student what are the units for accelleration, without the need for a number in front of the units.

The contextUnits.pl file is based on the contextExtensions.pl framework for extending a context to include new features. That means you can add units to existing contexts. For example, you could add units to the Matrix context to allow the entries of a matrix to have units.

See the POD documentation at the top of the file for more details about how to use the Units contexts.

Note that the contextExtension.pl is included in this PR, even though I made a separate PR for that file. Because the branch for that PR isn't in the PG repository, I can't target this PR to that one. If that PR is merged, the contextExtensions.pl file should disappear from this PR.

Copy link
Member

@drgrice1 drgrice1 left a comment

Choose a reason for hiding this comment

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

There are a couple of typos and POD issues noted.

This looks good, and will be a nice MathObject addition.

I did note one issue in testing that is related to issue #1092. If you use the Units context in a List it works nicely. However, if you use the LimitedUnits context with a List it does not. If Compute("3 deg, 4 deg") is called, the error "Can't use ',' in this context" is thrown. You can force it to work for creating the answer by calling List("3 deg", "4 deg"), but then if a student enters the answer "3 deg, 4 deg" the student gets the above error message. Perhaps there is some way to accomplish this using the contextExtension.pl macro? Although, it would be nice to not need additional steps for this.

macros/contexts/contextUnits.pl Outdated Show resolved Hide resolved
macros/contexts/contextUnits.pl Outdated Show resolved Hide resolved
macros/contexts/contextUnits.pl Outdated Show resolved Hide resolved
@dpvc
Copy link
Member Author

dpvc commented Sep 1, 2024

if you use the LimitedUnits context with a List it does not. If Compute("3 deg, 4 deg") is called, the error "Can't use ',' in this context" is thrown.

Yes, that is because the LimitedUnits context is based on the LimitedNumeric context, in which the comma has been removed (i.e., made undefined). There are several possible approaches to this.

One would be to leave it as is and document the fact that

$context->operators->redefine(',');

will make the comma available again. Alternatively, this could be added to the LimitedUnits context initially, though that does mean that it differs from how LimitedNumeric works. Finally, one could define a third context, LimitedUnits-List, corresponding to the LimitedNumeric-List that is in `contextLimitedNumeric.pl', that includes the comma operator.

Which route do you all think should be taken?

@dpvc
Copy link
Member Author

dpvc commented Sep 1, 2024

I've fixed the typos you identified. Thanks!

@dpvc dpvc added the Enhancement enhances the software label Sep 1, 2024
@drgrice1
Copy link
Member

drgrice1 commented Sep 1, 2024

I should have thought of that. I think you mentioned that before somewhere in a related setting. It works for me to add $context->operators->redefine(',');. Perhaps just make a comment on that in the documentation. Maybe others will like the other approaches better though?

Copy link
Member

@drgrice1 drgrice1 left a comment

Choose a reason for hiding this comment

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

Thanks for fixing the typos and pod issues, and thanks for this pull request to begin with!

@dpvc
Copy link
Member Author

dpvc commented Sep 3, 2024

I've updated this PR to include the changes to contextExtensions.pl that were made in the Fraction PR.

@pstaabp
Copy link
Member

pstaabp commented Sep 3, 2024

@dpvc Just tried one of your examples from the POD:

$u = Compute("meters per second per second");

I got the error

Division is allowed only for Numbers or a Vector, Point, or Matrix and a Number; see position 8 of formula at line 61 of [PG]/macros/core/Parser.pl

I think this is because of the second squared in the bottom. I had set the context to:

Context('Units')->withUnitsFor('length','velocity','time');

BTW, the shorthand Unit('m/s^2') worked fine.

@Alex-Jordan
Copy link
Contributor

Sometimes there is a question where the answer is like 5 %/yr or 0.05/yr.

  • Is the second format already possible with this?
  • Would it be possible for % to be a unit? Perhaps in its own category, or a "dimensionless" category? (I know there is a context for percent, but it doesn't help with units like %/yr.)
  • And then if % were a unit, is there any hope that 5% and 0.05 could be equivalent? Or5 %/yr and 0.05/yr?

I'm just thinking openly and not requesting these things. I'm interested in your assessment of how the structure you have here already could support these things, or if it would involve more.

@dpvc
Copy link
Member Author

dpvc commented Sep 4, 2024

Just tried one of your examples from the POD:

 $u = Compute("meters per second per second");

Argh. This was due to a last-minute reorganization of the initialization of the context, and it caused the per operator to get the wrong class definition. I've fixed it, so the example now works, as does Compute("m/s/s") and similar constructs.

@dpvc
Copy link
Member Author

dpvc commented Sep 4, 2024

@Alex-Jordan:

Is the second format already possible with this?

It wasn't but is now with the latest commit.

Would it be possible for % to be a unit?

You could add it using

Context("Units")->withUnitsFor("time")->addUnits("%" => {factor => .01, "%" => 1});

for example. Then you could do 5%/yr, but the formatting will be strange (5 %/yr as a string, and 5\,\frac{\%}{\text{yr}} in TeX). Also, you could do things like 5/% and 5 % yr and 5 yr/% and 5 %^2 and other such nonsense. Finally, 5 % and .05 will not be considered the same.

I don't think using % as a unit is the right approach. It's not a unit, it is a postfix unary operator (essentially /100). Adding units to the Percent context is the way to go, I think.

Context(context::Units::extending("Percent")->withUnitsFor("time"));

would allow 5%/yr and it will format properly. Further 5%/yr and .05/yr will be equivalent. And you won't be able to do the crazy things I mentioned above like 5 yr/%.

You are right that you can't do just %/yr this way, but if you need that as an answer, then you can use the technique of adding % to the Units context as in the first example above and make just that answer be in that specialized context.

@pstaabp
Copy link
Member

pstaabp commented Sep 6, 2024

@dpvc The latest commit fixed my other errors. Also testing your other example in the POD

   Context("Units")->addUnits(
      apples => { fruit => 1, aliases => ["apple"] },
      oranges => { fruit => 1, aliases => ["orange"] }
    );
    Compute("3 apples") == Compute("3 oranges"); # returns 1

isn't working for me. It all makes sense to me, but not getting the expected results.

@dpvc
Copy link
Member Author

dpvc commented Sep 6, 2024

It all makes sense to me, but not getting the expected results.

There was a typo in a variable name (when removing the aliases from the unit definition), and that meant the two definitions weren't the same. I had done my testing without the aliases, so didn't catch it. I pushed a commit to fix it.

@somiaj
Copy link
Contributor

somiaj commented Sep 17, 2024

Trying to test this out and running into some issues. First seems the category velocity isn't working directly.

Context("Units")->withUnitsFor("velocity");
$v = Compute("5 m/s");

This gives me the error that Variable 'm' is not defined in this context. But If I instead use Context("Units")->withUnitsFor("length", "time"); I no longer get any errors. Looking at the constants created, seems that velocity only adds c, knots, and mph. On the other hand if I only add the units for length and time, I can enter in "miles per hour" but not "mph".

Also in the TeX output, should units be wrapped in a \text{}, as I don't think units should be italicized like normal variables. And this maybe more a question for @drgrice1, but will there be a nice way to get MathQuill to play nicely with this without using the text mode button?

Last, is there a way to add all units to a problem, for if I write a problem and don't add all possible units I think a student might input as an answer, students who enter in units I didn't add to the context get the message, "foo is not defined in this context", which I don't think is that useful for students. Though having an message like "Answer is dimensionally incorrect" that could be toggled via some showDimensionWarnings flag I would find more useful.

@somiaj
Copy link
Contributor

somiaj commented Sep 17, 2024

Just to clarify my previous comment about MathQuill, it works just fine with units, I'm mostly talking about the formatting of units in the answer. Should they be italicized variables or not, and unsure if entering 'm/s' should show a slash, vs turn that into a fraction in the answer.

@somiaj
Copy link
Contributor

somiaj commented Sep 17, 2024

Also regarding TeX output, further testing seems to suggest that some times units are wrapped in \text{} and some are not. I see that miles per hour becomes \frac{\text{miles}}{\text{hour}}, but miles/hour is formatted just as written.

@dpvc
Copy link
Member Author

dpvc commented Sep 17, 2024

n the TeX output, should units be wrapped in a \text{}

All the units already should be in \text{} in TeX output, unless the unit definition provides its own TeX formatting.

but miles/hour is formatted just as written

I can't reproduce that; the TeX output is \frac{\text{miles}}{\text{hour}} for me, and I don't see how you would be able to not get that, as per and / both use the same class internally. Are you sure you were asking for TeX output and not string output?


seems the category velocity isn't working directly

It is actually doing what it is supposed to, as the various categories add the named units for that category, not all possible ways of obtaining compound units for that category. You are right that velocity only adds c, knots and mph, as those are the only named units in the Units.pm file that have m/s fundamental units. If you want allow m/s as an answer, you need to include the length and time units as well, as in

Context("Units")->withUnitsFor("velocity", "length", "time");
$v = Compute("5 m/s");

It would be possible to define the velocity category using

velocity => [{m => 1, s => -1}, {m => 1}, {s => 1}]

to get length and time as well, or to have withUnitsFor() include the individual categories for any fundamental units involved in a category, if people felt that it is too hard to specify the collections you want. I tried to hint at this with the example

Context("Units")->withUnitsFor("length", "volume");

so that you could do 5 m^3, but perhaps should have been more explicit about that.

it is possible that the category idea needs better handling. If there are other ideas about how to specify the units to be included, that would be worth a discussion.


Is there a way to add all units to a problem

You could do

Context("Units")->addUnitsNotAliases(keys %context::Units::Context::UNITS);

but I would not recommend it. One reason is that there are a number of single-letter units (A, c, F, G, g, H, h, J, K, L, m, N, S, s, T, v, W), and these can easily conflict with variables or other values within a problem. That is why I limited the units to the specific category that is needed rather than have them all defined all the time. Remember that these can be used internally in the problem (e.g., 3m + 5cm), not just at the end, like in the traditional NumberWithUnits() where conflicts with other variables would be minimized.


students who enter in units I didn't add to the context get the message, "[Variable] foo is not defined in this context",

Yes, that is correct, just as they would if they typed in some other letter that is not defined in the context. There is no way for the MathObject parser to know that the student might have been thinking about a unit rather than a function or variable or some other thing when it sees a letter that it doesn't have a definition for. I'm not sure how this could be handled better.

having an message like "Answer is dimensionally incorrect" that could be toggled via some showDimensionWarnings flag I would find more useful.

Perhaps in the LimitedUnits context, it would make sense to change the message about undefined variables to one about units, but remember that this will be given got any unknown letter (or string of letters), not just ones that are units that just aren't in this context.

Note that you do get a message about units being wrong if you enter units (that are defined) but are not correct, in case that is a help.

@dpvc
Copy link
Member Author

dpvc commented Sep 17, 2024

I did find a typo in the definition for the temperature category, and pushed a commit to fix it.

@somiaj
Copy link
Contributor

somiaj commented Sep 17, 2024

n the TeX output, should units be wrapped in a \text{}

All the units already should be in \text{} in TeX output, unless the unit definition provides its own TeX formatting.

but miles/hour is formatted just as written

I can't reproduce that; the TeX output is \frac{\text{miles}}{\text{hour}} for me, and I don't see how you would be able to not get that, as per and / both use the same class internally. Are you sure you were asking for TeX output and not string output?

With some more testing, I find that this is due to processing with inputted answers, as I was looking at the student preview answer TeX when typing answers into the answer box. If I do Compute("5 meters/second")->TeX, I get the units wrapped in \text{} correctly, but if I enter in "5 meters/second" into a MathQuill answer box (unsure if mathquill is the issue here or not), the answer preview is not wrapped in \text{}.

@somiaj
Copy link
Contributor

somiaj commented Sep 17, 2024

Here is a screenshot if it helps, as you can see the TeX inside the problem output is just fine, it is just the processing of the student answers that didn't get wrapped in \text{}.

image

@somiaj
Copy link
Contributor

somiaj commented Sep 17, 2024

Is there a way to add all units to a problem

You could do

Context("Units")->addUnitsNotAliases(keys %context::Units::Context::UNITS);

but I would not recommend it. One reason is that there are a number of single-letter units (A, c, F, G, g, H, h, J, K, L, m, N, S, s, T, v, W), and these can easily conflict with variables or other values within a problem. That is why I limited the units to the specific category that is needed rather than have them all defined all the time. Remember that these can be used internally in the problem (e.g., 3m + 5cm), not just at the end, like in the traditional NumberWithUnits() where conflicts with other variables would be minimized.

This makes sense, as I was considering a case where I just wanted a numerical answer with correct units, and didn't want to give students hints as to what the units might be, so I thought having a quick way to add all units would be nice.


having an message like "Answer is dimensionally incorrect" that could be toggled via some showDimensionWarnings flag I would find more useful.

Perhaps in the LimitedUnits context, it would make sense to change the message about undefined variables to one about units, but remember that this will be given got any unknown letter (or string of letters), not just ones that are units that just aren't in this context.

Yes, the case I was thinking of would be best done with some LimitedUnits context where I'm not expecting variables but want to be able to control if or if not hints are given to students who enter in dimensionally incorrect units.

Copy link
Member

@drgrice1 drgrice1 left a comment

Choose a reason for hiding this comment

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

Spell checking...

macros/contexts/contextUnits.pl Outdated Show resolved Hide resolved
macros/contexts/contextUnits.pl Outdated Show resolved Hide resolved
macros/contexts/contextUnits.pl Show resolved Hide resolved
macros/contexts/contextUnits.pl Outdated Show resolved Hide resolved
macros/contexts/contextUnits.pl Outdated Show resolved Hide resolved
macros/contexts/contextUnits.pl Outdated Show resolved Hide resolved
macros/contexts/contextUnits.pl Outdated Show resolved Hide resolved
macros/contexts/contextUnits.pl Outdated Show resolved Hide resolved
macros/contexts/contextUnits.pl Outdated Show resolved Hide resolved
macros/contexts/contextUnits.pl Outdated Show resolved Hide resolved
dpvc and others added 20 commits October 30, 2024 11:39
@dpvc
Copy link
Member Author

dpvc commented Oct 30, 2024

unsure if mathquill is the issue here

@somiaj, I'm pretty sure it is, but I don't have an installation running MathQuill to try it out. I'm not able to reproduce the problem without it, but don't have any idea why it would be producing this result. Does the MathQuill code set the preview_latex_string in its own, by any chance?

@drgrice1
Copy link
Member

unsure if mathquill is the issue here

@somiaj, I'm pretty sure it is, but I don't have an installation running MathQuill to try it out. I'm not able to reproduce the problem without it, but don't have any idea why it would be producing this result. Does the MathQuill code set the preview_latex_string in its own, by any chance?

This seems to be caused by the different precedence that is used when MathQuill is enabled. When MathQuill is enabled, then the precedence of the ' *' operator is 2.9 instead of 3.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Enhancement enhances the software
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants