From 190937bd2f5660ae8db71456ddbf5de22e9bc608 Mon Sep 17 00:00:00 2001 From: Lars-Dominik Braun Date: Sun, 29 Sep 2019 08:27:31 +0200 Subject: carpalx: Add model by Salvo et al. --- doc/Makefile | 3 +- lulua/carpalx.py | 103 +++++++++++++++++++++++++++++++++++++++++++++++++- lulua/optimize.py | 11 +++--- lulua/plot.py | 4 +- lulua/stats.py | 4 +- lulua/test_carpalx.py | 4 +- 6 files changed, 115 insertions(+), 14 deletions(-) diff --git a/doc/Makefile b/doc/Makefile index 0cd56a4..e280a53 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -5,6 +5,7 @@ WIKIEXTRACTOR:=../3rdparty/wikiextractor/WikiExtractor.py OPTROUNDS=100000 # pin layers, keep hand-optimized numbers, keep top row free OPTPINS='0;1;2;0,Bl1;0,Bl2;0,Bl3;0,Bl4;0,Bl5;0,Bl6;0,Bl7;0,Br6;0,Br5;0,Br4;0,Br3;0,Br2;0,Br1;3,Cl4;3,Cl3;3,Cl2;3,Cl1;3,Dl4;3,Dl3;3,Dl2;3,Dl1;3,El5;3,El4;3,El3;3,El2;3,Dl5;3,Cl5;3,El6' +OPTMODEL=mod01 all: ar-lulua.xmodmap ar-lulua.svg ar-asmo663.svg ar-linux.svg ar-malas.svg ar-phonetic.svg ar-osman.svg letterfreq.json ar-khorshid.svg @@ -56,5 +57,5 @@ $(STATSDIR)/stats.pickle: $(STATSDIR)/stats-arwiki.pickle \ ### optimization ### optimized.yaml: $(STATSDIR)/stats.pickle - lulua-optimize -n $(OPTROUNDS) -r -p $(OPTPINS) -l ar-lulua < $< > $@ + lulua-optimize -n $(OPTROUNDS) -r -p $(OPTPINS) -l ar-lulua -m $(OPTMODEL) < $< > $@ diff --git a/lulua/carpalx.py b/lulua/carpalx.py index 3e104bb..e17b6e4 100644 --- a/lulua/carpalx.py +++ b/lulua/carpalx.py @@ -43,8 +43,9 @@ from .keyboard import Button ModelParams = namedtuple ('ModelParams', ['kBPS', 'k123S', 'w0HRF', 'pHand', 'pRow', 'pFinger', 'fHRF', 'baselineEffort']) +models = dict ( # model parameters mod_01 from http://mkweb.bcgsc.ca/carpalx/?model_parameters -model01 = ModelParams ( +mod01 = ModelParams ( # k_b, k_p, k_s kBPS = (0.3555, 0.6423, 0.4268), # k_1, k_2, k_3 plus extension k_S (weight for simultaneous key presses) @@ -132,7 +133,105 @@ model01 = ModelParams ( 'Fr_altgr': 4.0, # XXX: dito }, - ) + ), + +# from paper “Ergonomic Keyboard Layout Designed for the Filipino Language” +salvo = ModelParams ( + # k_b, k_p, k_s + # XXX: unchanged from original? + kBPS = (0.3555, 0.6423, 0.4268), + # k_1, k_2, k_3 plus extension k_S (weight for simultaneous key presses) + # XXX: unchanged from original? + k123S = (1.0, 0.367, 0.235, 1.0), + # w0, wHand, wRow, wFinger + # XXX: unchanged from original? + w0HRF = (0.0, 1.0, 1.3088, 2.5948), + pHand = {LEFT: 0.0, RIGHT: 0.0}, + # numbers, top, base, bottom, control (XXX not part of original model) + # XXX: unchanged from original? + pRow = (1.5, 0.5, 0.0, 1.0, 1.5), + # symmetric penalties + # normalized as suggested by https://normanlayout.info/about.html + pFinger = { + LEFT: { + THUMB: 0.0, # XXX: not part of the original model + INDEX: 1-(6.09/6.57), + MIDDLE: 1-(5.65/6.57), + RING: 1-(4.54/6.57), + LITTLE: 1-(3.77/6.57), + }, + RIGHT: { + THUMB: 0.0, # XXX: not part of the original model + INDEX: 1-(6.57/6.57), + MIDDLE: 1-(6.37/6.57), + RING: 1-(5.08/6.57), + LITTLE: 1-(4.27/6.57), + }, + }, + # fHand, fRow, fFinger + fHRF = (1.0, 0.3, 0.3), + # baseline key effort + baselineEffort = { + # XXX: from model 01, not part of the paper + 'Bl1': 5.0, + 'Bl2': 5.0, + 'Bl3': 4.0, + 'Bl4': 4.0, + 'Bl5': 4.0, + 'Bl6': 3.5, + 'Bl7': 4.5, + 'Br6': 4.0, + 'Br5': 4.0, + 'Br4': 4.0, + 'Br3': 4.0, + 'Br2': 4.0, + 'Br1': 4.5, + + 'Cl1': 2.0, + 'Cl2': 2.0, + 'Cl3': 2.0, + 'Cl4': 2.0, + 'Cl5': 2.3, + 'Cr7': 3.0, + 'Cr6': 1.9, + 'Cr5': 2.0, + 'Cr4': 2.0, + 'Cr3': 2.2, + 'Cr2': 4.0, # XXX: dito + 'Cr1': 6.0, + + 'Dl_caps': 2.0, # XXX: dito + 'Dl1': 0.0, + 'Dl2': 0.0, + 'Dl3': 0.0, + 'Dl4': 0.0, + 'Dl5': 1.8, + 'Dr7': 1.8, + 'Dr6': 0.0, + 'Dr5': 0.0, + 'Dr4': 0.0, + 'Dr3': 0.0, + 'Dr2': 2.0, # XXX: dito + 'Dr1': 4.0, # XXX: dito + + 'El_shift': 4.0, # XXX: dito + 'El1': 4.0, # XXX: dito + 'El2': 2.0, + 'El3': 2.0, + 'El4': 2.0, + 'El5': 2.0, + 'El6': 3.5, + 'Er5': 2.0, + 'Er4': 2.0, + 'Er3': 2.0, + 'Er2': 2.0, + 'Er1': 2.0, + 'Er_shift': 4.0, # XXX: dito + + 'Fr_altgr': 4.0, # XXX: dito + }, + ), +) def madd (a, b): """ Given indexables a and b, computes a[0]*b[0]+a[1]*b[1]+… """ diff --git a/lulua/optimize.py b/lulua/optimize.py index 879f531..281f0ab 100644 --- a/lulua/optimize.py +++ b/lulua/optimize.py @@ -32,8 +32,7 @@ tqdm.get_lock().locks = [] import yaml from .layout import defaultLayouts, ButtonCombination, Layer, KeyboardLayout, GenericLayout -from .carpalx import Carpalx -from .carpalx import model01 as cmodel01 +from .carpalx import Carpalx, models, ModelParams from .writer import Writer from .util import first from .keyboard import defaultKeyboards, LetterButton @@ -147,8 +146,9 @@ class LayoutOptimizer (Annealer): triads: List[Tuple[ButtonCombination]], layout: KeyboardLayout, pins: FrozenSet[Tuple[int, Optional[Text]]], - writer: Writer): - carpalx = Carpalx (cmodel01, writer) + writer: Writer, + model: ModelParams): + carpalx = Carpalx (model, writer) super ().__init__ (LayoutOptimizerState (carpalx, buttonMap)) self.triads = triads @@ -256,6 +256,7 @@ def optimize (): parser.add_argument('-n', '--steps', type=int, default=10000, help='Number of iterations') parser.add_argument('-r', '--randomize', action='store_true', help='Randomize layout before optimizing') parser.add_argument('-p', '--pin', type=parsePin, help='Pin these layers/buttons') + parser.add_argument('-m', '--model', choices=list (models.keys()), default='mod01', help='Carpalx model') args = parser.parse_args() @@ -296,7 +297,7 @@ def optimize (): pins = [(x, keyboard[y] if y else None) for x, y in args.pin] - opt = LayoutOptimizer (buttonMap, triads, layout, pins, writer) + opt = LayoutOptimizer (buttonMap, triads, layout, pins, writer, model=models[args.model]) if args.randomize: logging.info ('randomizing initial layout') for i in range (len (buttonMap)*2): diff --git a/lulua/plot.py b/lulua/plot.py index 453c6eb..14b9732 100644 --- a/lulua/plot.py +++ b/lulua/plot.py @@ -28,7 +28,7 @@ from .layout import * from .keyboard import defaultKeyboards from .util import limit, displayText from .writer import Writer -from .carpalx import Carpalx, model01 +from .carpalx import Carpalx, models def letterfreq (args): """ Map key combinations to their text, bin it and plot sorted distribution """ @@ -124,7 +124,7 @@ def triadfreq (args): # letter-based binning, in case multiple buttons are mapped to the same # letter. - binned = defaultdict (lambda: dict (weight=0, effort=Carpalx (model01, writer), textTriad=None)) + binned = defaultdict (lambda: dict (weight=0, effort=Carpalx (models['mod01'], writer), textTriad=None)) weightSum = 0 for triad, weight in stats['triads'].triads.items (): textTriad = tuple (layout.getText (t) for t in triad) diff --git a/lulua/stats.py b/lulua/stats.py index 6689616..4cd20bd 100644 --- a/lulua/stats.py +++ b/lulua/stats.py @@ -26,7 +26,7 @@ from collections import defaultdict from .layout import * from .keyboard import defaultKeyboards from .writer import SkipEvent, Writer -from .carpalx import Carpalx, model01 as cmodel01 +from .carpalx import Carpalx, models from .plot import letterfreq, triadfreq def updateDictOp (a, b, op): @@ -217,7 +217,7 @@ def pretty (args): for triad, count in sorted (stats['triads'].triads.items (), key=itemgetter (1)): print (f'{triad} {count:10d}') - effort = Carpalx (cmodel01, writer) + effort = Carpalx (models['mod01'], writer) effort.addTriads (stats['triads'].triads) print ('total effort (carpalx)', effort.effort) diff --git a/lulua/test_carpalx.py b/lulua/test_carpalx.py index ac72a14..1d07763 100644 --- a/lulua/test_carpalx.py +++ b/lulua/test_carpalx.py @@ -20,7 +20,7 @@ import pytest -from .carpalx import Carpalx, model01, ModelParams +from .carpalx import Carpalx, models, ModelParams from .keyboard import defaultKeyboards from .layout import defaultLayouts, LEFT, RIGHT, INDEX, MIDDLE, RING, LITTLE from .writer import Writer @@ -101,7 +101,7 @@ def test_strokePath (t, i, expect): keyboard = defaultKeyboards['ibmpc105'] layout = defaultLayouts['ar-linux'].specialize (keyboard) writer = Writer (layout) - c = Carpalx (model01, writer) + c = Carpalx (models['mod01'], writer) t = tuple (map (keyboard.find, t)) assert c._strokePath (t)[i] == expect -- cgit v1.2.3