Skip to content

Commit

Permalink
Merge pull request #178 from /issues/122
Browse files Browse the repository at this point in the history
Issues/122
  • Loading branch information
YutoMizutani authored Nov 23, 2018
2 parents e2718f1 + bcd3f36 commit 726ba41
Show file tree
Hide file tree
Showing 66 changed files with 1,605 additions and 442 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,54 @@ import RxSwift
import RxCocoa
import OperantKit

public extension ObservableType {
func count() -> Observable<Int> {
return scan(0) { n, _ in n + 1 }
}

func getTime(_ timer: TimerUseCase) -> Observable<Milliseconds> {
return flatMap { _ in timer.elapsed() }
}

func response(_ timer: TimerUseCase) -> Observable<ResponseEntity> {
return Observable.zip(count(), getTime(timer))
.map { ResponseEntity.init(numOfResponses: $0.0, milliseconds: $0.1) }
}
}

public extension PublishSubject where E == Milliseconds {
var shared: Observable<E> {
return distinctUntilChanged()
.share(replay: 1)
}
}

///

public enum ExitCondition {
case reinforcement(Int)
case time(Milliseconds)
}

public protocol TrialParameter {
var schedules: [ScheduleUseCase] { get }
var exitCondition: ExitCondition { get }
}

public protocol TrialRecordable {
var startTime: Milliseconds { get set }
var startEntities: ResponseEntity { get set }
}

public protocol DiscreteTrialParameter {
var maxTrials: Int { get }
var trials: Matrix<TrialParameter> { get }
}

public protocol DiscreteTrialRecordable {
var trials: Matrix<TrialRecordable> { get }
}

/// Brown, P. L., & Jenkins, H. M. (1968). AUTO‐SHAPING OF THE PIGEON'S KEY‐PECK 1. Journal of the experimental analysis of behavior, 11(1), 1-8.
/// - DOI: https://dx.doi.org/10.1901/jeab.1968.11-1
/// - Available link: https://www.ncbi.nlm.nih.gov/pmc/articles/PMC1338436/
Expand All @@ -17,41 +65,32 @@ class BrownAndJenkins1968 {
let numberOfPairings: Int = 80
let whiteKeyLightDuration: Seconds = 8
let trayOperatingDuration: Milliseconds = 4000
let interTrialInterval: [Seconds] = [Seconds](30...90).filter({ $0 % 5 == 0 })
let interTrialInterval: [Seconds] = [Seconds](5...10) // (30...90)
.filter({ $0 % 5 == 0 })
.map { TimeUnit.seconds.milliseconds($0) }
.shuffled()

var nextInterval: Milliseconds = 0
var currentOrder: Int = 0
func updateInterval() {
nextInterval = TimeUnit.seconds.milliseconds(
interTrialInterval[Seconds.random(in: 0..<interTrialInterval.count)]
)
nextInterval = interTrialInterval[currentOrder % interTrialInterval.count]
}

let timer = WhileLoopTimerUseCase(priority: .high)
let schedule: ScheduleUseCase = FT(whiteKeyLightDuration)
let fixedTimeSchedule = FT(whiteKeyLightDuration)
let continuousReinforcementSchedule = CRF()
let responseAction = PublishSubject<Void>()
let startTimerAction = PublishSubject<Void>()
let finishTimerAction = PublishSubject<Void>()
var isSessionFlag = true
let duringSR = BehaviorRelay<Bool>(value: false)
var disposeBag = DisposeBag()

let numOfResponses = responseAction
.scan(0) { n, _ in n + 1 }
.asObservable()

let responseTimeMilliseconds = responseAction
.asObservable()
.flatMap { _ in timer.elapsed() }

Observable.zip(numOfResponses, responseTimeMilliseconds)
.map { ResponseEntity(numOfResponses: $0.0, milliseconds: $0.1) }
let respnseObservable = responseAction.response(timer)
.do(onNext: { print("Response: \($0.numOfResponses), \($0.milliseconds)ms") })
.subscribe()
.disposed(by: disposeBag)

let milliseconds = timer.milliseconds
let milliseconds = timer.milliseconds.shared
.filter({ $0 % 1000 == 0 })
.distinctUntilChanged()
.share(replay: 1)

let firstStart = milliseconds.take(1)

Expand All @@ -65,13 +104,18 @@ class BrownAndJenkins1968 {
.map { ResponseEntity(numOfResponses: 0, milliseconds: $0) }
.share(replay: 1)

let reinforcementOn = schedule.decision(timeObservable)
let reinforcementOn = Observable<ReinforcementResult>.merge(
fixedTimeSchedule.decision(timeObservable),
continuousReinforcementSchedule.decision(respnseObservable)
)
.filter({ _ in !duringSR.value })
.filter({ $0.isReinforcement })
.share(replay: 1)

let reinforcementOff = reinforcementOn
.do(onNext: { print("SR on: \($0.entity.milliseconds)ms (IRI: \(trayOperatingDuration)ms)") })
.extend(time: trayOperatingDuration, entities: schedule.extendEntity)
.storeResponse(fixedTimeSchedule.dataStore.lastReinforcementEntity, condition: { $0.isReinforcement })
.extend(time: trayOperatingDuration, entities: fixedTimeSchedule.extendEntity)
.flatMap { timer.delay(trayOperatingDuration, currentTime: $0.entity.milliseconds) }
.do(onNext: { print("SR off: \($0)ms") })
.asObservable()
Expand All @@ -80,10 +124,10 @@ class BrownAndJenkins1968 {
let nextTrial = Observable<Milliseconds>.merge(
firstStart,
reinforcementOff
)
)
.do(onNext: { _ in updateInterval() })
.do(onNext: { print("ITI on: \($0)ms (ITI: \(nextInterval)ms)") })
.extend(time: { nextInterval }, entities: schedule.extendEntity)
.do(onNext: { print("ITI on: \($0)ms (Next ITI: \(nextInterval)ms)") })
.extend(time: { nextInterval }, entities: fixedTimeSchedule.extendEntity)
.flatMap { timer.delay(nextInterval, currentTime: $0) }
.do(onNext: { print("ITI off: \($0)ms") })
.asObservable()
Expand All @@ -100,13 +144,20 @@ class BrownAndJenkins1968 {
.disposed(by: disposeBag)

reinforcementOff
.scan(0) { n, _ in n + 1 }
.count()
.do(onNext: { print("Trial \($0)/\(numberOfPairings) finished") })
.filter({ $0 >= numberOfPairings })
.mapToVoid()
.bind(to: finishTimerAction)
.disposed(by: disposeBag)

Observable<Bool>.merge(
reinforcementOn.map { _ in true },
reinforcementOff.map { _ in false }
)
.bind(to: duringSR)
.disposed(by: disposeBag)

startTimerAction
.flatMap { timer.start() }
.subscribe()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,7 @@ struct ExperimentFR {
let finishTimerAction = PublishSubject<Void>()
let disposeBag = DisposeBag()

let numOfResponses = responseAction
.scan(0) { n, _ in n + 1 }
.asObservable()

let milliseconds = responseAction
.asObservable()
.flatMap { _ in timer.elapsed() }

let response = Observable.zip(numOfResponses, milliseconds)
.map { ResponseEntity(numOfResponses: $0.0, milliseconds: $0.1) }
let response = responseAction.response(timer)
.do(onNext: { print("Response: \($0.numOfResponses), \($0.milliseconds)ms") })
.share(replay: 1)

Expand Down
Loading

0 comments on commit 726ba41

Please sign in to comment.