Skip to content

Commit

Permalink
Mostly everything measurement area related, #54
Browse files Browse the repository at this point in the history
  • Loading branch information
AgustinVallejo committed Jan 2, 2025
1 parent bc6e5ea commit 98a44a8
Show file tree
Hide file tree
Showing 13 changed files with 380 additions and 16 deletions.
5 changes: 4 additions & 1 deletion js/QuantumMeasurementStrings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,11 @@ type StringsType = {
'VStringProperty': LocalizedStringProperty;
'NStringProperty': LocalizedStringProperty;
'verticalStringProperty': LocalizedStringProperty;
'SGSubZStringProperty': LocalizedStringProperty;
'SGSubXStringProperty': LocalizedStringProperty;
'SGSubZStringProperty': LocalizedStringProperty;
'SSubXStringProperty': LocalizedStringProperty;
'SSubYStringProperty': LocalizedStringProperty;
'SSubZStringProperty': LocalizedStringProperty;
'SternGerlachMeasurementsStringProperty': LocalizedStringProperty;
'sourceModeStringProperty': LocalizedStringProperty;
'spinSourceStringProperty': LocalizedStringProperty;
Expand Down
59 changes: 57 additions & 2 deletions js/bloch-sphere/model/BlochSphereModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
* @author Agustín Vallejo (PhET Interactive Simulations)
*/

import BooleanProperty from '../../../../axon/js/BooleanProperty.js';
import Multilink from '../../../../axon/js/Multilink.js';
import NumberProperty from '../../../../axon/js/NumberProperty.js';
import Property from '../../../../axon/js/Property.js';
import Range from '../../../../dot/js/Range.js';
import TModel from '../../../../joist/js/TModel.js';
import { EmptySelfOptions } from '../../../../phet-core/js/optionize.js';
import PickRequired from '../../../../phet-core/js/types/PickRequired.js';
Expand All @@ -18,6 +20,7 @@ import EnumerationIO from '../../../../tandem/js/types/EnumerationIO.js';
import quantumMeasurement from '../../quantumMeasurement.js';
import { BlochSphereScene } from './BlochSphereScene.js';
import ComplexBlochSphere from './ComplexBlochSphere.js';
import { MeasurementBasis } from './MeasurementBasis.js';
import { StateDirection } from './StateDirection.js';

type SelfOptions = EmptySelfOptions;
Expand All @@ -29,6 +32,7 @@ export default class BlochSphereModel implements TModel {
public readonly selectedSceneProperty: Property<BlochSphereScene>;

public readonly preparationBlochSphere: ComplexBlochSphere;
public readonly singleMeasurementBlochSphere: ComplexBlochSphere;

// Coefficients of the state equation. They are derived from the Bloch Sphere representation on the multilink below.
// |psi> = upCoefficient |up> + downCoefficient * exp( i * phase * PI ) |down>
Expand All @@ -39,9 +43,21 @@ export default class BlochSphereModel implements TModel {
// Selected State Direction
public selectedStateDirectionProperty: Property<StateDirection>;

// Strength of the magnetic field
public magneticFieldStrengthProperty: NumberProperty;

// Time to measurement
public timeToMeasurementProperty: NumberProperty;

// Measurement basis
public measurementBasisProperty: Property<MeasurementBasis>;

// If is single or multiple measurement mode
public isSingleMeasurementModeProperty: BooleanProperty;

public constructor( providedOptions: QuantumMeasurementModelOptions ) {

this.selectedSceneProperty = new Property( BlochSphereScene.MEASUREMENT, {
this.selectedSceneProperty = new Property( BlochSphereScene.PRECESSION, {
tandem: providedOptions.tandem.createTandem( 'selectedSceneProperty' ),
phetioReadOnly: true,
phetioValueType: EnumerationIO( BlochSphereScene )
Expand All @@ -51,7 +67,13 @@ export default class BlochSphereModel implements TModel {
tandem: providedOptions.tandem.createTandem( 'preparationBlochSphere' )
} );

this.upCoefficientProperty = new NumberProperty( 1, {
this.singleMeasurementBlochSphere = new ComplexBlochSphere( {
initialRotationSpeed: 0.5,
tandem: providedOptions.tandem.createTandem( 'singleMeasurementBlochSphere' )
} );
this.singleMeasurementBlochSphere.polarAngleProperty.value = Math.PI / 2;

this.upCoefficientProperty = new NumberProperty( 0, {
tandem: providedOptions.tandem.createTandem( 'upCoefficientProperty' ),
phetioReadOnly: true
} );
Expand All @@ -72,6 +94,26 @@ export default class BlochSphereModel implements TModel {
phetioFeatured: true
} );

this.magneticFieldStrengthProperty = new NumberProperty( 1, {
tandem: providedOptions.tandem.createTandem( 'magneticFieldStrengthProperty' ),
range: new Range( -1, 1 )
} );

this.timeToMeasurementProperty = new NumberProperty( 0, {
tandem: providedOptions.tandem.createTandem( 'timeToMeasurementProperty' ),
range: new Range( 0, 1 )
} );

this.measurementBasisProperty = new Property( MeasurementBasis.S_SUB_Z, {
tandem: providedOptions.tandem.createTandem( 'measurementBasisProperty' ),
phetioValueType: EnumerationIO( MeasurementBasis )
} );

this.isSingleMeasurementModeProperty = new BooleanProperty( true, {
tandem: providedOptions.tandem.createTandem( 'isSingleMeasurementModeProperty' ),
phetioReadOnly: true
} );

let selectingStateDirection = false;
this.selectedStateDirectionProperty.link( stateDirection => {
if ( stateDirection !== StateDirection.CUSTOM ) {
Expand All @@ -97,6 +139,18 @@ export default class BlochSphereModel implements TModel {
}
}
);

this.magneticFieldStrengthProperty.link( magneticFieldStrength => {
this.singleMeasurementBlochSphere.rotatingSpeedProperty.value = magneticFieldStrength;
} );

this.preparationBlochSphere.polarAngleProperty.link( polarAngle => {
this.singleMeasurementBlochSphere.polarAngleProperty.value = polarAngle;
} );

this.preparationBlochSphere.azimuthalAngleProperty.link( azimuthalAngle => {
this.singleMeasurementBlochSphere.azimuthalAngleProperty.value = azimuthalAngle;
} );
}

/**
Expand All @@ -112,6 +166,7 @@ export default class BlochSphereModel implements TModel {
*/
public step( dt: number ): void {
this.preparationBlochSphere.step( dt );
this.singleMeasurementBlochSphere.step( dt );
}
}

Expand Down
13 changes: 8 additions & 5 deletions js/bloch-sphere/model/ComplexBlochSphere.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@

import NumberProperty from '../../../../axon/js/NumberProperty.js';
import Range from '../../../../dot/js/Range.js';
import optionize, { EmptySelfOptions } from '../../../../phet-core/js/optionize.js';
import Utils from '../../../../dot/js/Utils.js';
import optionize from '../../../../phet-core/js/optionize.js';
import { PhetioObjectOptions } from '../../../../tandem/js/PhetioObject.js';
import AbstractBlochSphere, { AbstractBlochSphereOptions } from '../../common/model/AbstractBlochSphere.js';
import quantumMeasurement from '../../quantumMeasurement.js';

type SelfOptions = EmptySelfOptions;
type SelfOptions = {
initialRotationSpeed?: number;
};

export type ComplexBlochSphereOptions = SelfOptions & AbstractBlochSphereOptions;

Expand All @@ -25,12 +28,12 @@ export default class ComplexBlochSphere extends AbstractBlochSphere {
public constructor( providedOptions?: ComplexBlochSphereOptions ) {

const options = optionize<ComplexBlochSphereOptions, SelfOptions, PhetioObjectOptions>()( {

initialRotationSpeed: 0
}, providedOptions );

super( options );

this.rotatingSpeedProperty = new NumberProperty( 0, {
this.rotatingSpeedProperty = new NumberProperty( options.initialRotationSpeed, {
range: new Range( -1, 1 ),
tandem: options.tandem.createTandem( 'rotatingSpeedProperty' ),
phetioReadOnly: true
Expand All @@ -43,7 +46,7 @@ export default class ComplexBlochSphere extends AbstractBlochSphere {
* Abstract method that should run calculations to update the Bloch Sphere representation.
*/
public override step( dt: number ): void {
this.azimuthalAngleProperty.value = ( this.azimuthalAngleProperty.value + this.rotatingSpeedProperty.value * dt ) % ( 2 * Math.PI );
this.azimuthalAngleProperty.value = Utils.moduloBetweenDown( this.azimuthalAngleProperty.value + this.rotatingSpeedProperty.value * dt, 0, 2 * Math.PI );
}

public override reset(): void {
Expand Down
30 changes: 30 additions & 0 deletions js/bloch-sphere/model/MeasurementBasis.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright 2024, University of Colorado Boulder

/**
* Contains the three posible directions the measurements can be performed in
*
* @author Agustín Vallejo
*/

import TReadOnlyProperty from '../../../../axon/js/TReadOnlyProperty.js';
import Enumeration from '../../../../phet-core/js/Enumeration.js';
import EnumerationValue from '../../../../phet-core/js/EnumerationValue.js';
import quantumMeasurement from '../../quantumMeasurement.js';
import QuantumMeasurementStrings from '../../QuantumMeasurementStrings.js';

export class MeasurementBasis extends EnumerationValue {
public static readonly S_SUB_X = new MeasurementBasis( QuantumMeasurementStrings.SSubXStringProperty, Math.PI / 2, 0, 'SSubX' );
public static readonly S_SUB_Y = new MeasurementBasis( QuantumMeasurementStrings.SSubYStringProperty, Math.PI / 2, Math.PI / 2, 'SSubY' );
public static readonly S_SUB_Z = new MeasurementBasis( QuantumMeasurementStrings.SSubZStringProperty, 0, 0, 'SSubZ' );

public static readonly enumeration = new Enumeration( MeasurementBasis );

public constructor( public readonly label: TReadOnlyProperty<string>,
public readonly polarAngle: number,
public readonly azimuthalAngle: number,
public readonly tandemName: string ) {
super();
}
}

quantumMeasurement.register( 'MeasurementBasis', MeasurementBasis );
181 changes: 181 additions & 0 deletions js/bloch-sphere/view/BlochSphereMeasurementArea.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
// Copyright 2024, University of Colorado Boulder

/**
* BlochSphereMeasurementArea is the node that contains the measurement area for the Bloch Sphere screen. It contains the
* UI elements for controlling magnetic field and basis of measurements...
*
* @author Agustín Vallejo
*/

import DerivedProperty from '../../../../axon/js/DerivedProperty.js';
import DerivedStringProperty from '../../../../axon/js/DerivedStringProperty.js';
import NumberProperty from '../../../../axon/js/NumberProperty.js';
import { Shape } from '../../../../kite/js/imports.js';
import { combineOptions, EmptySelfOptions } from '../../../../phet-core/js/optionize.js';
import Orientation from '../../../../phet-core/js/Orientation.js';
import WithRequired from '../../../../phet-core/js/types/WithRequired.js';
import PhetFont from '../../../../scenery-phet/js/PhetFont.js';
import { Node, NodeOptions, Path, RichText, RichTextOptions, Text, VBox } from '../../../../scenery/js/imports.js';
import AquaRadioButtonGroup from '../../../../sun/js/AquaRadioButtonGroup.js';
import RectangularRadioButtonGroup from '../../../../sun/js/buttons/RectangularRadioButtonGroup.js';
import Panel, { PanelOptions } from '../../../../sun/js/Panel.js';
import Slider from '../../../../sun/js/Slider.js';
import QuantumMeasurementColors from '../../common/QuantumMeasurementColors.js';
import QuantumMeasurementConstants from '../../common/QuantumMeasurementConstants.js';
import BlochSphereNode from '../../common/view/BlochSphereNode.js';
import QuantumMeasurementHistogram from '../../common/view/QuantumMeasurementHistogram.js';
import quantumMeasurement from '../../quantumMeasurement.js';
import BlochSphereModel from '../model/BlochSphereModel.js';
import { BlochSphereScene } from '../model/BlochSphereScene.js';
import { MeasurementBasis } from '../model/MeasurementBasis.js';
import BlochSphereNumericalEquationNode from './BlochSphereNumericalEquationNode.js';
import MagneticFieldArrowNode from './MagneticFieldArrowNode.js';
import MagneticFieldNode from './MagneticFieldNode.js';

type SelfOptions = EmptySelfOptions;

type BlochSphereMeasurementAreaOptions = SelfOptions & WithRequired<NodeOptions, 'tandem'>;

export default class BlochSphereMeasurementArea extends Node {

public constructor( model: BlochSphereModel, providedOptions: BlochSphereMeasurementAreaOptions ) {

super( providedOptions );

const equationNode = new BlochSphereNumericalEquationNode( model, {
tandem: providedOptions.tandem.createTandem( 'equationNode' ),
centerY: -50
} );
this.addChild( equationNode );

const magneticFieldNode = new MagneticFieldNode( model.magneticFieldStrengthProperty, {
visibleProperty: DerivedProperty.valueEqualsConstant( model.selectedSceneProperty, BlochSphereScene.PRECESSION )
} );
this.addChild( magneticFieldNode );

const singleMeasurementBlochSphereNode = new BlochSphereNode( model.singleMeasurementBlochSphere, {
tandem: providedOptions.tandem.createTandem( 'singleMeasurementBlochSphereNode' ),
drawTitle: false,
drawKets: false,
drawAngleIndicators: true,
centerX: magneticFieldNode.centerX,
centerY: magneticFieldNode.centerY
} );
this.addChild( singleMeasurementBlochSphereNode );

const spinUpLabelStringProperty = new DerivedStringProperty(
[ model.measurementBasisProperty ],
measurementBasis => measurementBasis.label.value + QuantumMeasurementConstants.SPIN_UP_ARROW_CHARACTER
);
const spinDownLabelStringProperty = new DerivedStringProperty(
[ model.measurementBasisProperty ],
measurementBasis => measurementBasis.label.value + QuantumMeasurementConstants.SPIN_DOWN_ARROW_CHARACTER
);

const measurementResultHistogram = new QuantumMeasurementHistogram(
new NumberProperty( 1 ),
new NumberProperty( 1 ),
[
new RichText( spinUpLabelStringProperty ),
new RichText( spinDownLabelStringProperty ) ],
{
tandem: providedOptions.tandem.createTandem( 'measurementResultHistogram' )
}
);

const radioButtonItems = [ true, false ].map(
isSingleMeasurement => {
return {
createNode: () => new Text( isSingleMeasurement ? 'Single' : 'Multiple', { font: new PhetFont( 16 ) } ),
value: isSingleMeasurement
};
} );
const singleOrMultipleRadioButtonGroup = new AquaRadioButtonGroup( model.isSingleMeasurementModeProperty, radioButtonItems, { orientation: 'vertical' } );


const basisRadioButtonTextOptions: RichTextOptions = {
font: new PhetFont( 18 ),
fill: 'black'
};
// Create and add the radio buttons that select the chart type view in the nuclideChartAccordionBox.
const basisRadioButtonGroupTandem = providedOptions.tandem.createTandem( 'basisRadioButtonGroup' );

const basisRadioGroupItems = MeasurementBasis.enumeration.values.map( basis => {
return {
value: basis,
createNode: () => new RichText(
basis.label, basisRadioButtonTextOptions
),
tandemName: `${basis.tandemName}RadioButton`
};
} );

const basisRadioButtonGroup = new RectangularRadioButtonGroup<MeasurementBasis>(
model.measurementBasisProperty, basisRadioGroupItems, {
orientation: 'horizontal',
tandem: basisRadioButtonGroupTandem,
phetioFeatured: true,
radioButtonOptions: {
baseColor: QuantumMeasurementColors.controlPanelFillColorProperty,
phetioVisiblePropertyInstrumented: false
}
} );

const measurementControlPanel = new Panel( new VBox( {
spacing: 10,
align: 'left',
children: [
new Text( 'Measurement Parameters', { font: new PhetFont( 18 ) } ),
singleOrMultipleRadioButtonGroup,
new Text( 'Basis', { font: new PhetFont( 18 ) } ),
basisRadioButtonGroup
]
} ), QuantumMeasurementConstants.panelOptions );

const measurementControls = new VBox( {
left: singleMeasurementBlochSphereNode.right + 20,
centerY: singleMeasurementBlochSphereNode.centerY,
spacing: 10,
children: [
measurementResultHistogram,
measurementControlPanel
]
} );
this.addChild( measurementControls );


const magneticFieldPanel = new Panel( new Node( {
children: [
new Node( {
centerX: 0,
centerY: 0,
children: [
new Path( new Shape().moveTo( 0, -50 ).lineTo( 0, 50 ), {
stroke: 'grey',
lineWidth: 1,
lineDash: [ 5, 5 ]
} ),
new MagneticFieldArrowNode( model.magneticFieldStrengthProperty )
]
} ),
new Slider( model.magneticFieldStrengthProperty, model.magneticFieldStrengthProperty.range, {
tandem: providedOptions.tandem.createTandem( 'magneticFieldStrengthSlider' ),
thumbFill: '#ff0',
orientation: Orientation.VERTICAL,
centerX: 50,
centerY: 0
} )
]
} ), combineOptions<PanelOptions>( {
top: singleMeasurementBlochSphereNode.bottom,
centerX: singleMeasurementBlochSphereNode.centerX,
visibleProperty: DerivedProperty.valueEqualsConstant( model.selectedSceneProperty, BlochSphereScene.PRECESSION )
}, QuantumMeasurementConstants.panelOptions ) );
this.addChild( magneticFieldPanel );

this.mutate( providedOptions );

}
}

quantumMeasurement.register( 'BlochSphereMeasurementArea', BlochSphereMeasurementArea );
1 change: 1 addition & 0 deletions js/bloch-sphere/view/BlochSpherePreparationArea.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ export default class BlochSpherePreparationArea extends VBox {
tandem: providedOptions.tandem.createTandem( 'blochSphereNode' ),
expandBounds: false,
drawTitle: false,
drawAngleIndicators: true,
scale: 0.9
} );

Expand Down
Loading

0 comments on commit 98a44a8

Please sign in to comment.