phetsims / calculus-grapher

"Calculus Grapher" is an educational simulation in HTML5, by PhET Interactive Simulations.
GNU General Public License v3.0
4 stars 4 forks source link

State Wrapper is setting `pointsProperty` for derived curves #327

Closed pixelzoom closed 1 year ago

pixelzoom commented 1 year ago

For https://github.com/phetsims/qa/issues/924 ...

While working on #325, I added this to Curve.ts:

this.pointsProperty.lazyLink( () => console.log( `${this.pointsProperty.phetioID}`  ) );

... and was dismayed to see that every curve (not just originalCurve and predictCurve) is having its pointsProperty set by the State Wrapper:

Getting the state took 265 ms.
Curve.ts:124 calculusGrapher.derivativeScreen.model.curves.originalCurve.pointsProperty
Curve.ts:124 calculusGrapher.derivativeScreen.model.curves.predictCurve.pointsProperty
Curve.ts:124 calculusGrapher.derivativeScreen.model.curves.derivative.pointsProperty
Curve.ts:124 calculusGrapher.integralScreen.model.curves.originalCurve.pointsProperty
Curve.ts:124 calculusGrapher.integralScreen.model.curves.predictCurve.pointsProperty
Curve.ts:124 calculusGrapher.integralScreen.model.curves.integral.pointsProperty
Curve.ts:124 calculusGrapher.advancedScreen.model.curves.originalCurve.pointsProperty
Curve.ts:124 calculusGrapher.advancedScreen.model.curves.predictCurve.pointsProperty
Curve.ts:124 calculusGrapher.advancedScreen.model.curves.derivative.pointsProperty
Curve.ts:124 calculusGrapher.advancedScreen.model.curves.integral.pointsProperty
Curve.ts:124 calculusGrapher.labScreen.model.curves.originalCurve.pointsProperty
Curve.ts:124 calculusGrapher.labScreen.model.curves.predictCurve.pointsProperty
Curve.ts:124 calculusGrapher.labScreen.model.curves.derivative.pointsProperty
Curve.ts:124 calculusGrapher.labScreen.model.curves.secondDerivative.pointsProperty
Curve.ts:124 calculusGrapher.labScreen.model.curves.integral.pointsProperty
Setting the state took 961 ms.

When we added thepointsProperty feature in https://github.com/phetsims/calculus-grapher/issues/90, our requirements were:

(1) Be able to set/get pointsProperty for originalCurve and predictCurve via Studio and wrappers (2) Be able to get pointsProperty for derived curves via wrappers only, and never set its value.

We are violating the 2nd requirements. The State Wrapper is setting pointsProperty for derived curves.

So in https://github.com/phetsims/calculus-grapher/issues/309, we elimintated the curveChangedEmitter that was called for derived curves, and impacting PhET-iO performance. But the value of pointsProperty is still being set for derived curves. While it's not currently causing a problem, ignoring a state change is a bad practice. And does it allow clients to set pointsProperty for derived curves?

If we added phetioState: false to pointsProperty for the derived curves, will this address the issue? And will we still be able to get (but not set) the value of pointsProperty via a wrapper?

pixelzoom commented 1 year ago

I'll need to discuss with @samreid or @zepumph, and will reach out to them on Slack.

@veillette FYI.

samreid commented 1 year ago

How can you be sure that the derived property value isn't just changing in response to other properties that change during the state set?

pixelzoom commented 1 year ago

Thanks for the reply @samreid . But I can tell by your question that I need to catch you up on the model and requirements. So this is an issue that will be more effective to discuss synchronously.

pixelzoom commented 1 year ago

I made this change to Curve.ts, to make pointsProperty have phetioState: false for derived curves:

    this.pointsProperty = new Property( initialPoints, {
      isValidValue: points => isValidPoints( initialPoints, points ),
      tandem: options.tandem.createTandem( 'pointsProperty' ),
      phetioValueType: ArrayIO( CurvePoint.CurvePointIO ),
      phetioReadOnly: options.pointsPropertyReadOnly,
+     phetioState: !options.pointsPropertyReadOnly,

In the State Wrapper, I verified that this eliminates get/set state for pointsProperty of the derived curves. The time to set state (as reported in the console) went from ~900ms to ~500ms, a huge performance boost.

In Studio, I switched Chrome dev tools to "JavaScript context: sim-frame", then typed this into the console. So I believe this means that clients can still request pointsProperty.value even if phetioState: false.

> phet.phetio.phetioEngine.phetioObjectMap[ 'calculusGrapher.derivativeScreen.model.curves.derivative.pointsProperty' ].value
< (1251) [CurvePoint, ...]
pixelzoom commented 1 year ago

Assuming that phetioState: false is the solution, I'm wondering about a couple of other things...

    this.pointsProperty = new Property( initialPoints, {
...
      phetioReadOnly: options. pointsAreDerived,
      phetioState: !options. pointsAreDerived,
    if ( !this.pointsProperty.phetioState ) {
      this.pointsProperty.lazyLink( () => {
        assert && assert( false, 'pointsProperty should not change if it is phetioState:false' );
      } );
    }
    if ( Tandem.PHET_IO_ENABLED && this.pointsProperty.isPhetioInstrumented() && !this.pointsProperty.phetioReadOnly ) {
      this.pointsProperty.lazyLink( () => {
        phet.log && phet.log( `pointsProperty changed: ${this.phetioID}` );
        this.curveChangedEmitter.emit();
      } );
    }

... be relocated to TransformedCurve.ts, like this:

    this.pointsProperty.lazyLink( () => this.curveChangedEmitter.emit() );
pixelzoom commented 1 year ago

@samreid said this is a better way of confirming that clients can get a value:

await phetioClient.invokeAsync( 'calculusGrapher.derivativeScreen.model.curves.derivative.pointsProperty', 'getValue' )
samreid commented 1 year ago

I think I understand why I was confused. calculusGrapher.derivativeScreen.model.curves.derivative.pointsProperty is actually not a DerivedProperty in the code, so that's why the value is going through. Properties that are phetioReadOnly: true still participate in state if they are phetioState: true, so you can consider whether calculusGrapher.derivativeScreen.model.curves.derivative.pointsProperty should be changed from Property to DerivedProperty.

pixelzoom commented 1 year ago

... you can consider whether calculusGrapher.derivativeScreen.model.curves.derivative.pointsProperty should be changed from Property to DerivedProperty.

That's not going to work with the current architecture. For derived curves, it's now a Property that can take only 1 value, is phetioReadOnly: true, and phetioState: false.

pixelzoom commented 1 year ago

Changes committed to master and 1.0 in the above commits.

In addition to reducing the time to set state by 44% (from ~900ms to ~500 ms, for unbuilt), the API file is now 37% smaller (from 6.12MB to 3.85MB).

@veillette would you please have a look, to check my work? If you run the State Wrapper with ?log, it will identify the pointsProperty elements that are changed when setting state.

Assign back to me when done.

veillette commented 1 year ago

It is a nice performance improvement for the state wrapper. I ran it with ?log and only the predictCurve and originalCurve are getting changed, as intended.

veillette commented 1 year ago

I also reviewed the commits. Assigning back to @pixelzoom for cherry-picking.

zepumph commented 1 year ago

@samreid, will you please create a separate issue in the phet-io repo if you think that we should investigate having phetioReadOnly respected by the state wrapper? I believe it will open up a can of worms, because we need to support setting implementation details.

samreid commented 1 year ago

@samreid, will you please create a separate issue in the phet-io repo if you think that we should investigate having phetioReadOnly respected by the state wrapper?

I believe values with phetioState: true, phetioReadOnly: true should be restored during via setState. To remove something from the state, mark it as phetioState: false or to prevent something from being set during setState, use phetioType: DerivedPropertyIO or a similar pattern in a custom IO Type.

pixelzoom commented 1 year ago

Cherry-picking was done above, in https://github.com/phetsims/calculus-grapher/commit/f8179ab8c70ddd1f5ef3facf40702027cd48ee7c, https://github.com/phetsims/calculus-grapher/commit/bc73aa51c5caf8bedc49afaa3c7e19feb4d44ab7, and https://github.com/phetsims/phet-io-sim-specific/commit/e4189f9ebfa8e22ebdd4d748c04fd98d6be59197.

This is a relatively major change. But it's isolated to 2 files. So we'll continue with the current RC test https://github.com/phetsims/qa/issues/924, and verify in the next RC. I'll summarize what/how to test in a forthcoming comment.

zepumph commented 1 year ago

Any updates to doc needed?

pixelzoom commented 1 year ago

@zepumph said:

Any updates to doc needed?

Yes - what you suggested in https://github.com/phetsims/calculus-grapher/issues/327#issuecomment-1490757337 sounds great. Tracking in https://github.com/phetsims/phet-io/issues/1927.

pixelzoom commented 1 year ago

To verify in the next RC:

In PhET brand, verify that:

In Studio, verify that:

In the State Wrapper, verify that:

Nancy-Salpepi commented 1 year ago

Things looks good in rc.2:

In PhET brand, verify that:

In Studio, verify that:

In the State Wrapper, verify that: