Skip to content

Commit

Permalink
Logic for the SG model, see #53
Browse files Browse the repository at this point in the history
  • Loading branch information
AgustinVallejo committed Oct 15, 2024
1 parent 36f58ac commit 974c1b0
Show file tree
Hide file tree
Showing 7 changed files with 120 additions and 30 deletions.
16 changes: 8 additions & 8 deletions js/spin/model/SpinExperiment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import EnumerationValue from '../../../../phet-core/js/EnumerationValue.js';
import quantumMeasurement from '../../quantumMeasurement.js';


type SternGerlachOptions = {
type SternGerlachExperimentSetting = {
isZOriented: boolean;
active: boolean;
};
Expand All @@ -27,22 +27,22 @@ export default class SpinExperiment extends EnumerationValue {
public static readonly EXPERIMENT_2 = new SpinExperiment( 'Experiment 2 [SGx]', [
{ isZOriented: false, active: true }
] );
public static readonly EXPERIMENT_3 = new SpinExperiment( 'Experiment 3 [Sz, Sx]', [
public static readonly EXPERIMENT_3 = new SpinExperiment( 'Experiment 3 [SGz, SGx]', [
{ isZOriented: true, active: true },
{ isZOriented: false, active: true },
{ isZOriented: false, active: true }
] );
public static readonly EXPERIMENT_4 = new SpinExperiment( 'Experiment 4 [Sz, Sz]', [
public static readonly EXPERIMENT_4 = new SpinExperiment( 'Experiment 4 [SGz, SGz]', [
{ isZOriented: true, active: true },
{ isZOriented: true, active: true },
{ isZOriented: true, active: true }
] );
public static readonly EXPERIMENT_5 = new SpinExperiment( 'Experiment 5 [Sx, Sz]', [
public static readonly EXPERIMENT_5 = new SpinExperiment( 'Experiment 5 [SGx, SGz]', [
{ isZOriented: false, active: true },
{ isZOriented: true, active: true },
{ isZOriented: true, active: true }
] );
public static readonly EXPERIMENT_6 = new SpinExperiment( 'Experiment 6 [Sx, Sx]', [
public static readonly EXPERIMENT_6 = new SpinExperiment( 'Experiment 6 [SGx, SGx]', [
{ isZOriented: false, active: true },
{ isZOriented: false, active: true },
{ isZOriented: false, active: true }
Expand All @@ -57,12 +57,12 @@ export default class SpinExperiment extends EnumerationValue {

public readonly experimentName: string | TReadOnlyProperty<string>;

public readonly experimentSettings: SternGerlachOptions[];
public readonly experimentSetting: SternGerlachExperimentSetting[];

public constructor( experimentName: string | TReadOnlyProperty<string>, experimentSettings: SternGerlachOptions[] ) {
public constructor( experimentName: string | TReadOnlyProperty<string>, experimentSetting: SternGerlachExperimentSetting[] ) {
super();
this.experimentName = experimentName;
this.experimentSettings = experimentSettings;
this.experimentSetting = experimentSetting;
}
}

Expand Down
51 changes: 44 additions & 7 deletions js/spin/model/SpinModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
* @author Agustín Vallejo
*/

import Emitter from '../../../../axon/js/Emitter.js';
import NumberProperty from '../../../../axon/js/NumberProperty.js';
import Property from '../../../../axon/js/Property.js';
import TReadOnlyProperty from '../../../../axon/js/TReadOnlyProperty.js';
Expand Down Expand Up @@ -49,13 +50,13 @@ export class SpinValue extends EnumerationValue {
}

export class SourceMode extends EnumerationValue {

public static readonly SINGLE = new SourceMode( 'Single Particle', 'singleParticle' );

public static readonly CONTINUOUS = new SourceMode( 'Continuous', 'continuous' );

public static readonly enumeration = new Enumeration( SourceMode );

public constructor( public readonly sourceName: string | TReadOnlyProperty<string>, public readonly tandemName: string ) {
super();
}
Expand All @@ -78,6 +79,10 @@ export default class SpinModel implements TModel {

public readonly particleAmmountProperty: NumberProperty;

// Used to update the opacities of the particle rays
// TODO: Better naming?? https://github.com/phetsims/quantum-measurement/issues/53
public readonly probabilitiesUpdatedEmitter: Emitter = new Emitter();

public constructor( providedOptions: QuantumMeasurementModelOptions ) {

this.sourceModeProperty = new Property<SourceMode>( SourceMode.SINGLE );
Expand All @@ -97,15 +102,24 @@ export default class SpinModel implements TModel {

this.currentExperimentProperty.link( experiment => {
sternGerlachModels.forEach( ( sternGerlachModel, index ) => {
if ( experiment.experimentSettings.length > index ) {
if ( experiment.experimentSetting.length > index ) {
// TODO: Should visibility be only handled via the View? https://github.com/phetsims/quantum-measurement/issues/53
sternGerlachModel.isVisibleProperty.set( experiment.experimentSettings[ index ].active );
sternGerlachModel.isZOrientedProperty.set( experiment.experimentSettings[ index ].isZOriented );
sternGerlachModel.isVisibleProperty.set( experiment.experimentSetting[ index ].active );
sternGerlachModel.isZOrientedProperty.set( experiment.experimentSetting[ index ].isZOriented );
}
else {
sternGerlachModel.isVisibleProperty.set( false );
}
} );

// Set the probabilities of the experiment. In the continuous case, this immediately alters the shown rays
// In the single case, this prepares the probabilities for the particle that will be shot
// TODO: Given the above description, is measure() a correct name? https://github.com/phetsims/quantum-measurement/issues/53
this.measure();
} );

this.blochSphere.spinStateProperty.link( () => {
this.measure();
} );

this.currentlyShootingParticlesProperty = new Property<boolean>( false, {
Expand All @@ -120,6 +134,29 @@ export default class SpinModel implements TModel {

}

public measure(): void {
const experimentSetting = this.currentExperimentProperty.value.experimentSetting;

// Measure on the first SG, this will change its upProbabilityProperty
this.firstSternGerlachModel.measure( this.blochSphere.spinStateProperty.value );

if ( experimentSetting.length > 1 ) {
// Measure on the second SG according to the orientation of the first one
this.secondSternGerlachModel.measure(
// SG1 passes the up-spin particles to SG2
this.firstSternGerlachModel.isZOrientedProperty.value ? SpinValue.Z_PLUS : SpinValue.X_PLUS
);

this.thirdSternGerlachModel.measure(
// SG1 passes the down-spin particles to SG3, and because X- is not in the initial spin values, we pass null
this.firstSternGerlachModel.isZOrientedProperty.value ? SpinValue.Z_MINUS : null
);

}

this.probabilitiesUpdatedEmitter.emit();
}

/**
* Resets the model.
*/
Expand Down
36 changes: 36 additions & 0 deletions js/spin/model/SternGerlachModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,23 @@
*/

import BooleanProperty from '../../../../axon/js/BooleanProperty.js';
import DerivedProperty from '../../../../axon/js/DerivedProperty.js';
import NumberProperty from '../../../../axon/js/NumberProperty.js';
import TReadOnlyProperty from '../../../../axon/js/TReadOnlyProperty.js';
import Vector2 from '../../../../dot/js/Vector2.js';
import Tandem from '../../../../tandem/js/Tandem.js';
import quantumMeasurement from '../../quantumMeasurement.js';
import { SpinValue } from './SpinModel.js';

export default class SternGerlachModel {

public readonly isZOrientedProperty: BooleanProperty;

public readonly isVisibleProperty: BooleanProperty;

public readonly upProbabilityProperty: NumberProperty;
public readonly downProbabilityProperty: TReadOnlyProperty<number>;

public constructor( isZOriented: boolean, tandem: Tandem ) {

this.isZOrientedProperty = new BooleanProperty( isZOriented, {
Expand All @@ -29,8 +37,36 @@ export default class SternGerlachModel {
tandem: tandem.createTandem( 'isVisibleProperty' )
} );

this.upProbabilityProperty = new NumberProperty( 0.5, {
tandem: tandem.createTandem( 'upProbabilityProperty' )
} );

this.downProbabilityProperty = new DerivedProperty( [ this.upProbabilityProperty ], upProbability => 1 - upProbability );

}


/**
* Measures incoming particles with a given state (Z+, Z-, X+), and returns the probability of spin up
* in the direction of the Stern Gerlach experiment.
*/
public measure( incomingState: SpinValue | null ): void {

// Using a XZ vector to calculate the projected probability.
// The experiment has a measurement vector and the incoming state has a spin vector
// Based on the dot product we'll obtain the probability

const incomingStateVector = incomingState === SpinValue.Z_PLUS ? new Vector2( 0, 1 ) :
incomingState === SpinValue.Z_MINUS ? new Vector2( 0, -1 ) :
incomingState === SpinValue.X_PLUS ? new Vector2( 1, 0 ) : new Vector2( -1, 0 );

const experimentMeasurementVector = this.isZOrientedProperty.value ? new Vector2( 0, 1 ) : new Vector2( 1, 0 );

// <Z|Z> = 1, <Z|X> = 0, <-Z|Z> = -1 so we need to re-scale into the [0, 1] range
this.upProbabilityProperty.value = ( incomingStateVector.dot( experimentMeasurementVector ) + 1 ) / 2;
}


public reset(): void {
// no-op TODO https://github.com/phetsims/quantum-measurement/issues/53
}
Expand Down
14 changes: 1 addition & 13 deletions js/spin/view/ParticleRayPath.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
* @author Agustín Vallejo
*/

import Multilink from '../../../../axon/js/Multilink.js';
import TReadOnlyProperty from '../../../../axon/js/TReadOnlyProperty.js';
import Vector2 from '../../../../dot/js/Vector2.js';
import { Shape } from '../../../../kite/js/imports.js';
Expand Down Expand Up @@ -36,8 +35,6 @@ export default class ParticleRayPath extends Node {
lineCap: 'round'
};

let rayOpacity = 1;

// Create 7 paths for the possible rays
const rayPaths = _.range( 7 ).map( i => {
return new Path( null, rayPathOptions );
Expand All @@ -60,7 +57,7 @@ export default class ParticleRayPath extends Node {
const mappedRayPoints = rayPointsPair.map( point => this.globalToLocalPoint( point ) );
rayPaths[ i ].shape = new Shape().moveTo( mappedRayPoints[ 0 ].x, mappedRayPoints[ 0 ].y )
.lineTo( mappedRayPoints[ 1 ].x, mappedRayPoints[ 1 ].y );
rayPaths[ i ].opacity = rayOpacity;
rayPaths[ i ].opacity = 1;
}
else {
rayPaths[ i ].shape = null;
Expand All @@ -80,15 +77,6 @@ export default class ParticleRayPath extends Node {
}
}
};

// TODO: Multilink for when we have another value in the model for the opacity of secondary and tertiary rays https://github.com/phetsims/quantum-measurement/issues/53
Multilink.multilink(
[ particleAmmountProperty ],
particleAmmount => {
rayOpacity = particleAmmount;
this.updateOpacities( _.times( 7, () => rayOpacity ) );
}
);
}
}

Expand Down
30 changes: 29 additions & 1 deletion js/spin/view/SpinMeasurementArea.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import SternGerlachNode from './SternGerlachNode.js';

export default class SpinMeasurementArea extends VBox {

public readonly particleRayPath: ParticleRayPath;

public constructor( model: SpinModel, parentNode: Node, layoutBounds: Bounds2, tandem: Tandem ) {

const items: ComboBoxItem<SpinExperiment>[] = SpinExperiment.enumeration.values.map( experiment => {
Expand All @@ -46,6 +48,8 @@ export default class SpinMeasurementArea extends VBox {
tandem.createTandem( 'particleRayPath' )
);

// Since this is a VBox, we add the ray path to the container node
// TODO: Currently parent is screenVies, Will this cause trouble if an intermediate parent is added? see https://github.com/phetsims/quantum-measurement/issues/53
parentNode.addChild( particleRayPath );

super( {
Expand Down Expand Up @@ -74,6 +78,28 @@ export default class SpinMeasurementArea extends VBox {
margin: 20
} );

this.particleRayPath = particleRayPath;

const updateRayOpacities = ( particleAmmount: number ) => {
particleRayPath.updateOpacities( [
particleAmmount, // First ray only depends on the initial particle ammount
model.firstSternGerlachModel.upProbabilityProperty.value * particleAmmount,
model.firstSternGerlachModel.downProbabilityProperty.value * particleAmmount,
model.firstSternGerlachModel.upProbabilityProperty.value * model.secondSternGerlachModel.upProbabilityProperty.value * particleAmmount,
model.firstSternGerlachModel.upProbabilityProperty.value * model.secondSternGerlachModel.downProbabilityProperty.value * particleAmmount,
model.firstSternGerlachModel.downProbabilityProperty.value * model.thirdSternGerlachModel.upProbabilityProperty.value * particleAmmount,
model.firstSternGerlachModel.downProbabilityProperty.value * model.thirdSternGerlachModel.downProbabilityProperty.value * particleAmmount
] );
};

model.particleAmmountProperty.link( particleAmmount => {
updateRayOpacities( particleAmmount );
} );

model.probabilitiesUpdatedEmitter.addListener( () => {
updateRayOpacities( model.particleAmmountProperty.value );
} );

model.currentExperimentProperty.link( experiment => {

// Adjust the global positions of the nodes
Expand All @@ -87,7 +113,7 @@ export default class SpinMeasurementArea extends VBox {

const endOfRays = layoutBounds.maxX * 5;

if ( experiment.experimentSettings.length === 1 ) {
if ( experiment.experimentSetting.length === 1 ) {
particleRayPath.updatePaths( [
primaryRayPoints,
[ firstSternGerlachNode.topExitGlobalPosition, new Vector2( endOfRays, firstSternGerlachNode.topExitGlobalPosition.y ) ],
Expand All @@ -105,6 +131,8 @@ export default class SpinMeasurementArea extends VBox {
[ thirdSternGerlachNode.bottomExitGlobalPosition, new Vector2( endOfRays, thirdSternGerlachNode.bottomExitGlobalPosition.y ) ]
] );
}

updateRayOpacities( model.particleAmmountProperty.value );
} );
}
}
Expand Down
1 change: 1 addition & 0 deletions js/spin/view/SpinScreenView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export default class SpinScreenView extends QuantumMeasurementScreenView {
this.mockupOpacityProperty && this.mockupOpacityProperty.link( opacity => {
spinStatePreparationArea.opacity = 1 - opacity;
spinMeasurementArea.opacity = 1 - opacity;
spinMeasurementArea.particleRayPath.opacity = 1 - opacity;
} );

model.currentExperimentProperty.notifyListenersStatic();
Expand Down
2 changes: 1 addition & 1 deletion js/spin/view/SternGerlachNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export default class SternGerlachNode extends Node {
new RichText( new DerivedProperty(
[ experimentModel.isZOrientedProperty ],
( isZOriented: boolean ) => isZOriented ? 'SG<sub>Z' : 'SG<sub>X' ),
{ font: new PhetFont( 16 ), fill: 'white', center: new Vector2( 25, 80 ) } )
{ font: new PhetFont( 20 ), fill: 'white', center: new Vector2( 25, 80 ) } )
]
} );
}
Expand Down

0 comments on commit 974c1b0

Please sign in to comment.