Skip to content

Commit

Permalink
improve distinction between classical and quantum system behavior, see
Browse files Browse the repository at this point in the history
  • Loading branch information
jbphet committed Oct 2, 2024
1 parent 2377427 commit 02bc92d
Show file tree
Hide file tree
Showing 12 changed files with 184 additions and 116 deletions.
4 changes: 2 additions & 2 deletions js/coins/model/CoinsExperimentSceneModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,8 @@ export default class CoinsExperimentSceneModel extends PhetioObject {
if ( preparingExperiment ) {

// Set the coin measurement states back to their initial values.
this.singleCoin.prepareInstantly();
this.coinSet.prepareInstantly();
this.singleCoin.prepareNow();
this.coinSet.prepareNow();
}
else {

Expand Down
20 changes: 11 additions & 9 deletions js/coins/model/ExperimentMeasurementState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,27 @@

/**
* ExperimentMeasurementState is a string union that functions as an enumeration of the possible states for a basic
* measurement experiment. It essentially encodes two pieces of information, one being whether the state of the
* experiment has been measured and the other being whether the state is hidden or shown. Because the combination
* of not measured and shown is not possible, this amounts to three states. See the comments below for each state for
* more details.
* measurement experiment. The possible values of the states are intended to support the measurement of both classical
* and quantum systems. The states used and the transitions between them will be somewhat different for classical
* versus quantum systems.
*
* @author John Blanco (PhET Interactive Simulations)
*/

export const ExperimentMeasurementStateValues = [

// The experiment is prepared and can be measured, but hasn't been yet.
'readyToBeMeasured',

// The experiment is in the process of preparing to be measured. For example, in the case of a coin flipping
// experiment, this is the state when the coin is being flipped.
'preparingToBeMeasured',

// The experiment's state has been measured and revealed to the observer(s).
'measuredAndRevealed'
// The experiment is prepared and can be measured, but hasn't been yet. This state only applies to quantum systems.
'readyToBeMeasured',

// The experiment is in a state where the values are determined but are not currently being shown to the user.
'measuredAndHidden',

// The experiment's state has been revealed to the observer(s).
'revealed'

] as const;

Expand Down
21 changes: 10 additions & 11 deletions js/coins/view/CoinExperimentButtonSet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ import PickRequired from '../../../../phet-core/js/types/PickRequired.js';
import PhetFont from '../../../../scenery-phet/js/PhetFont.js';
import { Color, NodeOptions, VBox, VBoxOptions } from '../../../../scenery/js/imports.js';
import TextPushButton, { TextPushButtonOptions } from '../../../../sun/js/buttons/TextPushButton.js';
import { SystemType } from '../../common/model/SystemType.js';
import TwoStateSystem from '../../common/model/TwoStateSystem.js';
import TwoStateSystemSet from '../../common/model/TwoStateSystemSet.js';
import quantumMeasurement from '../../quantumMeasurement.js';
import QuantumMeasurementStrings from '../../QuantumMeasurementStrings.js';
Expand All @@ -31,8 +29,7 @@ const BUTTON_WIDTH = 160; // empirically determined to match spec

export default class CoinExperimentButtonSet extends VBox {

public constructor( system: TwoStateSystem<string> | TwoStateSystemSet<string>,
systemType: SystemType,
public constructor( system: TwoStateSystemSet<string>,
testBoxReadyProperty: TProperty<boolean>,
providedOptions: CoinExperimentButtonSetOptions ) {

Expand Down Expand Up @@ -62,10 +59,10 @@ export default class CoinExperimentButtonSet extends VBox {
],
( hideString, revealString, observeString, experimentState ) => {
let labelString;
if ( experimentState === 'measuredAndRevealed' ) {
if ( experimentState === 'revealed' ) {
labelString = hideString;
}
else if ( systemType === 'classical' ) {
else if ( system.systemType === 'classical' ) {
labelString = revealString;
}
else {
Expand All @@ -80,10 +77,12 @@ export default class CoinExperimentButtonSet extends VBox {
revealHideButtonTextProperty,
combineOptions<TextPushButtonOptions>( commonButtonOptions, {
listener: () => {
if ( system.measurementStateProperty.value === 'readyToBeMeasured' ) {
system.measure();
if ( system.measurementStateProperty.value === 'readyToBeMeasured' ||
system.measurementStateProperty.value === 'measuredAndHidden' ) {

system.reveal();
}
else if ( system.measurementStateProperty.value === 'measuredAndRevealed' ) {
else if ( system.measurementStateProperty.value === 'revealed' ) {
system.hide();
}
},
Expand All @@ -92,7 +91,7 @@ export default class CoinExperimentButtonSet extends VBox {
);

const flipOrReprepareButton = new TextPushButton(
systemType === 'classical' ?
system.systemType === 'classical' ?
QuantumMeasurementStrings.flipStringProperty :
QuantumMeasurementStrings.reprepareStringProperty,
combineOptions<TextPushButtonOptions>( commonButtonOptions, {
Expand All @@ -102,7 +101,7 @@ export default class CoinExperimentButtonSet extends VBox {
);

const flipOrReprepareAndRevealButton = new TextPushButton(
systemType === 'classical' ?
system.systemType === 'classical' ?
QuantumMeasurementStrings.flipAndRevealStringProperty :
QuantumMeasurementStrings.reprepareAndRevealStringProperty,
combineOptions<TextPushButtonOptions>( commonButtonOptions, {
Expand Down
10 changes: 4 additions & 6 deletions js/coins/view/CoinExperimentMeasurementArea.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import MultiCoinTestBox from './MultiCoinTestBox.js';
import MultipleCoinAnimations from './MultipleCoinAnimations.js';
import SceneSectionHeader from './SceneSectionHeader.js';
import SingleCoinAnimations from './SingleCoinAnimations.js';
import TwoStateSystem from '../../common/model/TwoStateSystem.js';
import SingleCoinTestBox from './SingleCoinTestBox.js';

const RADIO_BUTTON_FONT = new PhetFont( 12 );
Expand Down Expand Up @@ -68,8 +67,7 @@ export default class CoinExperimentMeasurementArea extends VBox {

// Create the buttons that will be used to control the single-coin test box.
const singleCoinExperimentButtonSet = new CoinExperimentButtonSet(
sceneModel.singleCoin as TwoStateSystem<string>,
sceneModel.systemType,
sceneModel.singleCoin,
singleCoinInTestBoxProperty,
{
tandem: tandem.createTandem( 'singleCoinExperimentButtonSet' ),
Expand All @@ -96,6 +94,7 @@ export default class CoinExperimentMeasurementArea extends VBox {
sceneModel.coinSet,
sceneModel.coinSet.measurementStateProperty,
sceneModel.coinSet.numberOfActiveSystemsProperty,
sceneModel.coinSet.measuredDataChanged,
{ tandem: tandem.createTandem( 'multipleCoinTestBox' ) }
);
const multiCoinExperimentHistogram = new CoinMeasurementHistogram( sceneModel.coinSet, sceneModel.systemType, {
Expand All @@ -104,7 +103,6 @@ export default class CoinExperimentMeasurementArea extends VBox {
} );
const multipleCoinExperimentButtonSet = new CoinExperimentButtonSet(
sceneModel.coinSet,
sceneModel.systemType,
coinSetInTestBoxProperty,
{
tandem: tandem.createTandem( 'multipleCoinExperimentButtonSet' ),
Expand Down Expand Up @@ -202,7 +200,7 @@ export default class CoinExperimentMeasurementArea extends VBox {
this.measuredCoinsPixelRepresentation.setY( offset );

sceneModel.coinSet.measurementStateProperty.link( measurementState => {
if ( measurementState === 'measuredAndRevealed' ) {
if ( measurementState === 'revealed' ) {
this.measuredCoinsPixelRepresentation.redraw( sceneModel.coinSet.measuredValues );
}
} );
Expand All @@ -218,7 +216,7 @@ export default class CoinExperimentMeasurementArea extends VBox {
visibleProperty: new DerivedProperty(
[ sceneModel.preparingExperimentProperty, sceneModel.singleCoin.measurementStateProperty ],
( preparingExperiment, singleCoinExperimentState ) =>
!preparingExperiment && singleCoinExperimentState !== 'measuredAndRevealed'
!preparingExperiment && singleCoinExperimentState !== 'revealed'
)
} );
singleCoinTestBox.clippedTestBox.addChild( coinMask );
Expand Down
6 changes: 3 additions & 3 deletions js/coins/view/CoinMeasurementHistogram.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export default class CoinMeasurementHistogram extends Node {
// Create a Property that controls whether the values should be displayed.
const displayValuesProperty = DerivedProperty.valueEqualsConstant(
coinSet.measurementStateProperty,
'measuredAndRevealed'
'revealed'
);

// Create the X and Y axes.
Expand Down Expand Up @@ -116,7 +116,7 @@ export default class CoinMeasurementHistogram extends Node {
const testValue = systemType === 'classical' ? 'heads' : 'up';
let total = 0;

if ( measurementState === 'measuredAndRevealed' ) {
if ( measurementState === 'revealed' ) {
_.times( numberOfActiveSystems, i => {
if ( coinSet.measuredValues[ i ] === testValue ) {
total++;
Expand All @@ -134,7 +134,7 @@ export default class CoinMeasurementHistogram extends Node {
const testValue = systemType === 'classical' ? 'tails' : 'down';
let total = 0;

if ( measurementState === 'measuredAndRevealed' ) {
if ( measurementState === 'revealed' ) {
_.times( numberOfActiveSystems, i => {
if ( coinSet.measuredValues[ i ] === testValue ) {
total++;
Expand Down
2 changes: 1 addition & 1 deletion js/coins/view/CoinSetPixelRepresentation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ export default class CoinSetPixelRepresentation extends CanvasNode {
'transparent';
};
break;
case 'measuredAndRevealed':
case 'revealed':
getColor = ( value: number ) => {
return value === 1 ? 'black' : 'fuchsia';
};
Expand Down
16 changes: 10 additions & 6 deletions js/coins/view/MultiCoinTestBox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import quantumMeasurement from '../../quantumMeasurement.js';
import { MAX_COINS, MULTI_COIN_EXPERIMENT_QUANTITIES } from '../model/CoinsExperimentSceneModel.js';
import { ExperimentMeasurementState } from '../model/ExperimentMeasurementState.js';
import SmallCoinNode from './SmallCoinNode.js';
import TEmitter from '../../../../axon/js/TEmitter.js';

type SelfOptions = EmptySelfOptions;
export type MultiCoinTestBoxOptions = SelfOptions & WithRequired<HBoxOptions, 'tandem'>;
Expand All @@ -45,6 +46,7 @@ export default class MultiCoinTestBox extends HBox {
public constructor( coinSet: TwoStateSystemSet<string>,
measurementStateProperty: Property<ExperimentMeasurementState>,
numberOfActiveSystemsProperty: TReadOnlyProperty<number>,
measuredDataChangedEmitter: TEmitter,
providedOptions?: MultiCoinTestBoxOptions ) {

// Create the main rectangle that will represent the area where the coins will be hidden and shown.
Expand Down Expand Up @@ -88,18 +90,20 @@ export default class MultiCoinTestBox extends HBox {
measurementStateProperty.link( measurementState => {

// Make the box look hazy when the measurement is not revealed.
multipleCoinTestBoxRectangle.fill = measurementState === 'measuredAndRevealed' ?
multipleCoinTestBoxRectangle.fill = measurementState === 'revealed' ?
TEST_BOX_CONTENTS_REVEALED_FILL :
TEST_BOX_CONTENTS_HIDDEN_FILL;

// Update the appearance of the coin nodes.
this.updateCoinNodes( coinSet, measurementState );
} );

// When phet-io state is being set, it is possible for the coin states to change without there being any change in
// the state of the experiment. The following code makes sure that coin nodes are updated if necessary.
isSettingPhetioStateProperty.lazyLink( isSettingPhetioState => {
if ( !isSettingPhetioState && measurementStateProperty.value === 'measuredAndRevealed' ) {
// Update the appearance of the coin nodes when the data changes.
measuredDataChangedEmitter.addListener( () => {

// When phet-io state is being set, the measured data can change without any change to the measurement state of
// the coin set. This makes sure that the coin nodes are updated in that situation.
if ( isSettingPhetioStateProperty.value ) {
this.updateCoinNodes( coinSet, measurementStateProperty.value );
}
} );
Expand All @@ -113,7 +117,7 @@ export default class MultiCoinTestBox extends HBox {
private updateCoinNodes( coinSet: TwoStateSystemSet<string>, measurementState: ExperimentMeasurementState ): void {
this.residentCoinNodes.forEach( ( coinNode, index ) => {

if ( measurementState === 'measuredAndRevealed' ) {
if ( measurementState === 'revealed' ) {

if ( coinNode.isFlipping ) {
coinNode.stopFlipping();
Expand Down
2 changes: 1 addition & 1 deletion js/coins/view/MultipleCoinAnimations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ public readonly startIngressAnimationForCoinSet: ( forReprepare: boolean ) => vo
multipleCoinTestBox.addCoinNodeToBox( coinNode );

if ( sceneModel.systemType === 'quantum' ) {
sceneModel.coinSet.prepareInstantly();
sceneModel.coinSet.prepareNow();
}

// If all animations have completed, set the flag that indicates the coins are fully in the box.
Expand Down
2 changes: 1 addition & 1 deletion js/coins/view/SingleCoinAnimations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ export default class SingleCoinAnimations {
// "Collapse" the state of the coin node so that it shows a single state, not a superposed one.
const quantumCoinNode = singleCoinNode as QuantumCoinNode;
quantumCoinNode.showSuperpositionProperty.value = false;
sceneModel.singleCoin.prepareInstantly();
sceneModel.singleCoin.prepareNow();
}

// The coin is in the test box, so update the flag that makes this known.
Expand Down
2 changes: 1 addition & 1 deletion js/coins/view/SingleCoinTestBox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export default class SingleCoinTestBox extends Node {

// Make the box transparent when the state of the measurement indicates that the coin is revealed to the user.
measurementStateProperty.link( singleCoinMeasurementState => {
testBoxRectangle.fill = singleCoinMeasurementState === 'measuredAndRevealed' ?
testBoxRectangle.fill = singleCoinMeasurementState === 'revealed' ?
Color.TRANSPARENT :
SINGLE_COIN_TEST_BOX_UNREVEALED_FILL;
} );
Expand Down
18 changes: 6 additions & 12 deletions js/common/model/TwoStateSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import NumberProperty from '../../../../axon/js/NumberProperty.js';
import optionize, { EmptySelfOptions } from '../../../../phet-core/js/optionize.js';
import quantumMeasurement from '../../quantumMeasurement.js';
import TwoStateSystemSet, { StateSetMeasurementResult, TwoStateSystemSetOptions } from './TwoStateSystemSet.js';
import TwoStateSystemSet, { TwoStateSystemSetOptions } from './TwoStateSystemSet.js';
import StrictOmit from '../../../../phet-core/js/types/StrictOmit.js';
import Property from '../../../../axon/js/Property.js';
import StringUnionIO from '../../../../tandem/js/types/StringUnionIO.js';
Expand All @@ -25,7 +25,7 @@ export default class TwoStateSystem<T extends string> extends TwoStateSystemSet<
public readonly measuredValueProperty: Property<T | null>;

public constructor( stateValues: readonly T[],
initialState: T | null,
initialState: T,
biasProperty: NumberProperty,
providedOptions: TwoStateSystemOptions ) {

Expand All @@ -40,17 +40,11 @@ export default class TwoStateSystem<T extends string> extends TwoStateSystemSet<
phetioValueType: NullableIO( StringUnionIO( stateValues ) ),
validValues: [ ...stateValues, null ]
} );
}

/**
* Override the measure function
*/
public override measure(): StateSetMeasurementResult<T> {
const measurementResults = super.measure();
assert && assert( measurementResults.length === 1, 'unexpected length for measurement set' );
assert && assert( measurementResults.measuredValues[ 0 ] !== null, 'measurement result should not be indeterminate' );
this.measuredValueProperty.set( measurementResults.measuredValues[ 0 ] );
return measurementResults;
// Hook up to the data-changed emitter to update the data Property.
this.measuredDataChanged.addListener( () => {
this.measuredValueProperty.value = this.measuredValues[ 0 ];
} );
}

/**
Expand Down
Loading

0 comments on commit 02bc92d

Please sign in to comment.