Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ClassCastException when comparing hl.Locus to row key #13046

Closed
danking opened this issue May 12, 2023 · 5 comments
Closed

ClassCastException when comparing hl.Locus to row key #13046

danking opened this issue May 12, 2023 · 5 comments
Assignees
Labels

Comments

@danking
Copy link
Contributor

danking commented May 12, 2023

What happened?

Details here: https://discuss.hail.is/t/subset-matrix-table-to-a-medium-sized-list-of-variants/3362/5

Java stack trace:
java.lang.ClassCastException: class org.apache.spark.sql.catalyst.expressions.GenericRow cannot be cast to class is.hail.variant.Locus (org.apache.spark.sql.catalyst.expressions.GenericRow is in unnamed module of loader 'app'; is.hail.variant.Locus is in unnamed module of loader org.apache.spark.util.MutableURLClassLoader @62435e70)
        at is.hail.expr.JSONAnnotationImpex$.exportAnnotation(AnnotationImpex.scala:124)
        at is.hail.expr.JSONAnnotationImpex$.$anonfun$exportAnnotation$5(AnnotationImpex.scala:129)
        at is.hail.expr.JSONAnnotationImpex$.$anonfun$exportAnnotation$5$adapted(AnnotationImpex.scala:128)
        at scala.collection.generic.GenTraversableFactory.tabulate(GenTraversableFactory.scala:150)
        at is.hail.expr.JSONAnnotationImpex$.exportAnnotation(AnnotationImpex.scala:128)
        at is.hail.types.virtual.Type.toJSON(Type.scala:184)
        at is.hail.expr.JSONAnnotationImpex$.$anonfun$exportAnnotation$4(AnnotationImpex.scala:125)
        at is.hail.utils.Interval.toJSON(Interval.scala:103)
        at is.hail.expr.JSONAnnotationImpex$.exportAnnotation(AnnotationImpex.scala:125)
        at is.hail.expr.JSONAnnotationImpex$.$anonfun$exportAnnotation$1(AnnotationImpex.scala:113)
        at scala.collection.TraversableLike.$anonfun$map$1(TraversableLike.scala:238)
        at scala.collection.IndexedSeqOptimized.foreach(IndexedSeqOptimized.scala:36)
        at scala.collection.IndexedSeqOptimized.foreach$(IndexedSeqOptimized.scala:33)
        at scala.collection.mutable.WrappedArray.foreach(WrappedArray.scala:38)
        at scala.collection.TraversableLike.map(TraversableLike.scala:238)
        at scala.collection.TraversableLike.map$(TraversableLike.scala:231)
        at scala.collection.AbstractTraversable.map(Traversable.scala:108)
        at is.hail.expr.JSONAnnotationImpex$.exportAnnotation(AnnotationImpex.scala:113)
        at is.hail.expr.ir.Pretty.header(Pretty.scala:405)
        at is.hail.expr.ir.Pretty.pretty$1(Pretty.scala:463)
        at is.hail.expr.ir.Pretty.$anonfun$sexprStyle$4(Pretty.scala:453)
        at scala.collection.Iterator$$anon$10.next(Iterator.scala:459)
        at scala.collection.Iterator$ConcatIterator.next(Iterator.scala:230)
        at is.hail.utils.richUtils.RichIterator$$anon$3.next(RichIterator.scala:67)
        at is.hail.utils.prettyPrint.Doc$.advance$1(PrettyPrintWriter.scala:68)
        at is.hail.utils.prettyPrint.Doc$.render(PrettyPrintWriter.scala:139)
        at is.hail.utils.prettyPrint.Doc.render(PrettyPrintWriter.scala:163)
        at is.hail.utils.prettyPrint.Doc.render(PrettyPrintWriter.scala:167)
        at is.hail.expr.ir.Pretty.sexprStyle(Pretty.scala:466)
        at is.hail.expr.ir.Pretty.apply(Pretty.scala:429)
        at is.hail.expr.ir.Pretty$.apply(Pretty.scala:22)
        at is.hail.expr.ir.Optimize$.apply(Optimize.scala:45)
        at is.hail.expr.ir.lowering.OptimizePass.transform(LoweringPass.scala:30)
        at is.hail.expr.ir.lowering.LoweringPass.$anonfun$apply$3(LoweringPass.scala:16)
        at is.hail.utils.ExecutionTimer.time(ExecutionTimer.scala:81)
        at is.hail.expr.ir.lowering.LoweringPass.$anonfun$apply$1(LoweringPass.scala:16)
        at is.hail.utils.ExecutionTimer.time(ExecutionTimer.scala:81)
        at is.hail.expr.ir.lowering.LoweringPass.apply(LoweringPass.scala:14)
        at is.hail.expr.ir.lowering.LoweringPass.apply$(LoweringPass.scala:13)
        at is.hail.expr.ir.lowering.OptimizePass.apply(LoweringPass.scala:26)
        at is.hail.expr.ir.lowering.LoweringPipeline.$anonfun$apply$1(LoweringPipeline.scala:15)
        at is.hail.expr.ir.lowering.LoweringPipeline.$anonfun$apply$1$adapted(LoweringPipeline.scala:13)
        at scala.collection.IndexedSeqOptimized.foreach(IndexedSeqOptimized.scala:36)
        at scala.collection.IndexedSeqOptimized.foreach$(IndexedSeqOptimized.scala:33)
        at scala.collection.mutable.WrappedArray.foreach(WrappedArray.scala:38)
        at is.hail.expr.ir.lowering.LoweringPipeline.apply(LoweringPipeline.scala:13)
        at is.hail.expr.ir.CompileAndEvaluate$._apply(CompileAndEvaluate.scala:47)
        at is.hail.backend.spark.SparkBackend._execute(SparkBackend.scala:450)
        at is.hail.backend.spark.SparkBackend.$anonfun$executeEncode$2(SparkBackend.scala:486)
        at is.hail.backend.ExecuteContext$.$anonfun$scoped$3(ExecuteContext.scala:70)
        at is.hail.utils.package$.using(package.scala:635)
        at is.hail.backend.ExecuteContext$.$anonfun$scoped$2(ExecuteContext.scala:70)
        at is.hail.utils.package$.using(package.scala:635)
        at is.hail.annotations.RegionPool$.scoped(RegionPool.scala:17)
        at is.hail.backend.ExecuteContext$.scoped(ExecuteContext.scala:59)
        at is.hail.backend.spark.SparkBackend.withExecuteContext(SparkBackend.scala:339)
        at is.hail.backend.spark.SparkBackend.$anonfun$executeEncode$1(SparkBackend.scala:483)
        at is.hail.utils.ExecutionTimer$.time(ExecutionTimer.scala:52)
        at is.hail.backend.spark.SparkBackend.executeEncode(SparkBackend.scala:482)
        at jdk.internal.reflect.GeneratedMethodAccessor60.invoke(Unknown Source)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:566)
        at py4j.reflection.MethodInvoker.invoke(MethodInvoker.java:244)
        at py4j.reflection.ReflectionEngine.invoke(ReflectionEngine.java:357)
        at py4j.Gateway.invoke(Gateway.java:282)
        at py4j.commands.AbstractCommand.invokeMethod(AbstractCommand.java:132)
        at py4j.commands.CallCommand.execute(CallCommand.java:79)
        at py4j.GatewayConnection.run(GatewayConnection.java:238)
        at java.base/java.lang.Thread.run(Thread.java:829)



Hail version: 0.2.105-acd89e80c345
Error summary: ClassCastException: class org.apache.spark.sql.catalyst.expressions.GenericRow cannot be cast to class is.hail.variant.Locus (org.apache.spark.sql.catalyst.expressions.GenericRow is in unnamed module of loader 'app'; is.hail.variant.Locus is in unnamed module of loader org.apache.spark.util.MutableURLClassLoader @62435e70)

Version

0.2.105

Relevant log output

No response

@danking danking added the bug label May 17, 2023
@tpoterba tpoterba self-assigned this May 17, 2023
@ehigham ehigham assigned ehigham and unassigned tpoterba Jul 6, 2023
@ehigham
Copy link
Member

ehigham commented Jul 6, 2023

I've eventually reproduced this. I was a bit thrown that the error message in this issue is different to the one in the op. Here's my work so far:

# create `variants` heterogeneous array as described in the op
variants = [["10", 123, "G", "C"], ["10", 456, "T", "A"]]

# not sure how the `mt` was created, but not sure it's important for the
# purposes of reproducing the failure
mt = hl.struct(
  locus=hl.locus(contig='10', pos=60515, reference_genome='GRCh37'),
  alleles=['C', 'T']
)

expr = hl.any(
  lambda x:
    (mt.locus.contig == hl.literal(x[0])) & \
    (mt.locus.position == hl.literal(int(x[1]))) & \
    (mt.alleles == hl.literal(x[2:])),
  variants
)

hl.eval(expr)

This fails in the call to any with the following:

Traceback (most recent call last):
  File "/home/edmund/.local/src/hail/hail/python/hail/expr/expressions/expression_typecheck.py", line 78, in check
    return self.coerce(to_expr(x))
  File "/home/edmund/.local/src/hail/hail/python/hail/expr/expressions/base_expression.py", line 275, in to_expr
    return cast_expr(e, dtype, partial_type)
  File "/home/edmund/.local/src/hail/hail/python/hail/expr/expressions/base_expression.py", line 281, in cast_expr
    dtype = impute_type(e, partial_type)
  File "/home/edmund/.local/src/hail/hail/python/hail/expr/expressions/base_expression.py", line 129, in impute_type
    t = _impute_type(x, partial_type=partial_type)
  File "/home/edmund/.local/src/hail/hail/python/hail/expr/expressions/base_expression.py", line 179, in _impute_type
    ts = {_impute_type(element, partial_type.element_type) for element in x}
  File "/home/edmund/.local/src/hail/hail/python/hail/expr/expressions/base_expression.py", line 179, in <setcomp>
    ts = {_impute_type(element, partial_type.element_type) for element in x}
  File "/home/edmund/.local/src/hail/hail/python/hail/expr/expressions/base_expression.py", line 182, in _impute_type
    raise ExpressionException("Hail does not support heterogeneous arrays: "
hail.expr.expressions.base_expression.ExpressionException: Hail does not support heterogeneous arrays: found list with elements of types [dtype('int32'), dtype('str')] 

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/edmund/.local/src/hail/hail/python/hail/typecheck/check.py", line 584, in arg_check
    return checker.check(arg, function_name, arg_name)
  File "/home/edmund/.local/src/hail/hail/python/hail/expr/expressions/expression_typecheck.py", line 80, in check
    raise TypecheckFailure from e
hail.typecheck.check.TypecheckFailure

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/edmund/.pyenv/versions/3.8.16/lib/python3.8/runpy.py", line 194, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "/home/edmund/.pyenv/versions/3.8.16/lib/python3.8/runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "/home/edmund/.vscode/extensions/ms-python.python-2023.10.1/pythonFiles/lib/python/debugpy/adapter/../../debugpy/launcher/../../debugpy/__main__.py", line 39, in <module>
    cli.main()
  File "/home/edmund/.vscode/extensions/ms-python.python-2023.10.1/pythonFiles/lib/python/debugpy/adapter/../../debugpy/launcher/../../debugpy/../debugpy/server/cli.py", line 430, in main
    run()
  File "/home/edmund/.vscode/extensions/ms-python.python-2023.10.1/pythonFiles/lib/python/debugpy/adapter/../../debugpy/launcher/../../debugpy/../debugpy/server/cli.py", line 284, in run_file
    runpy.run_path(target, run_name="__main__")
  File "/home/edmund/.vscode/extensions/ms-python.python-2023.10.1/pythonFiles/lib/python/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_runpy.py", line 321, in run_path
    return _run_module_code(code, init_globals, run_name,
  File "/home/edmund/.vscode/extensions/ms-python.python-2023.10.1/pythonFiles/lib/python/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_runpy.py", line 135, in _run_module_code
    _run_code(code, mod_globals, init_globals,
  File "/home/edmund/.vscode/extensions/ms-python.python-2023.10.1/pythonFiles/lib/python/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_runpy.py", line 124, in _run_code
    exec(code, run_globals)
  File "/home/edmund/.local/src/hail/test.py", line 10, in <module>
    expr = hl.any(lambda x:
  File "/home/edmund/.local/src/hail/hail/python/hail/expr/functions.py", line 3530, in any
    collection = arg_check(args[1], 'any', 'collection', collection_type)
  File "/home/edmund/.local/src/hail/hail/python/hail/typecheck/check.py", line 586, in arg_check
    raise TypeError("{fname}: parameter '{argname}': "
TypeError: any: parameter 'collection': expected expression of type set<any> or array<any>, found list: [['10', 123, 'G', 'C'], ['10', 456, 'T', 'A']]

So, hail doesn't support heterogeneous arrays. Converting to a homogeneous array:

variants = [("10", 123, ["G", "C"]), ("10", 456, ["T", "A"])]

expr = hl.any(
  lambda x:
    (mt.locus.contig == hl.literal(x[0])) & \
    (mt.locus.position == hl.literal(int(x[1]))) & \
    (mt.alleles == hl.literal(x[2])),
  variants
)

hl.eval(expr)

Leads to the following error (which looks like the bug!):

Traceback (most recent call last):
  File "test.py", line 10, in <module>
    expr = hl.any(lambda x:
  File "/home/edmund/.local/src/hail/hail/python/hail/expr/functions.py", line 3531, in any
    return collection.any(f)
  File "<decorator-gen-510>", line 2, in any
  File "/home/edmund/.local/src/hail/hail/python/hail/typecheck/check.py", line 577, in wrapper
    return __original_func(*args_, **kwargs_)
  File "/home/edmund/.local/src/hail/hail/python/hail/expr/expressions/typed_expressions.py", line 68, in any
    return hl.array(self).fold(lambda accum, elt: accum | f(elt), False)
  File "<decorator-gen-518>", line 2, in fold
  File "/home/edmund/.local/src/hail/hail/python/hail/typecheck/check.py", line 577, in wrapper
    return __original_func(*args_, **kwargs_)
  File "/home/edmund/.local/src/hail/hail/python/hail/expr/expressions/typed_expressions.py", line 221, in fold
    return collection._to_stream().fold(lambda x, y: f(x, y), zero)
  File "<decorator-gen-650>", line 2, in fold
  File "/home/edmund/.local/src/hail/hail/python/hail/typecheck/check.py", line 577, in wrapper
    return __original_func(*args_, **kwargs_)
  File "/home/edmund/.local/src/hail/hail/python/hail/expr/expressions/typed_expressions.py", line 4522, in fold
    body = to_expr(f(accum_ref, elt_ref))
  File "/home/edmund/.local/src/hail/hail/python/hail/typecheck/check.py", line 364, in f
    ret = x(*args)
  File "/home/edmund/.local/src/hail/hail/python/hail/expr/expressions/typed_expressions.py", line 221, in <lambda>
    return collection._to_stream().fold(lambda x, y: f(x, y), zero)
  File "/home/edmund/.local/src/hail/hail/python/hail/typecheck/check.py", line 364, in f
    ret = x(*args)
  File "/home/edmund/.local/src/hail/hail/python/hail/expr/expressions/typed_expressions.py", line 68, in <lambda>
    return hl.array(self).fold(lambda accum, elt: accum | f(elt), False)
  File "/home/edmund/.local/src/hail/hail/python/hail/typecheck/check.py", line 364, in f
    ret = x(*args)
  File "/home/edmund/.local/src/hail/hail/python/hail/typecheck/check.py", line 364, in f
    ret = x(*args)
  File "test.py", line 11, in <lambda>
    (mt.locus.contig == hl.literal(x[0])) & \
  File "<decorator-gen-690>", line 2, in literal
  File "/home/edmund/.local/src/hail/hail/python/hail/typecheck/check.py", line 577, in wrapper
    return __original_func(*args_, **kwargs_)
  File "/home/edmund/.local/src/hail/hail/python/hail/expr/functions.py", line 261, in literal
    return literal(hl.eval(to_expr(x, dtype)), dtype)
  File "<decorator-gen-668>", line 2, in eval
  File "/home/edmund/.local/src/hail/hail/python/hail/typecheck/check.py", line 577, in wrapper
    return __original_func(*args_, **kwargs_)
  File "/home/edmund/.local/src/hail/hail/python/hail/expr/expressions/expression_utils.py", line 223, in eval
    return eval_timed(expression)[0]
  File "<decorator-gen-666>", line 2, in eval_timed
  File "/home/edmund/.local/src/hail/hail/python/hail/typecheck/check.py", line 577, in wrapper
    return __original_func(*args_, **kwargs_)
  File "/home/edmund/.local/src/hail/hail/python/hail/expr/expressions/expression_utils.py", line 189, in eval_timed
    return _eval_many(expression, timed=True, name='eval_timed')[0]
  File "/home/edmund/.local/src/hail/hail/python/hail/expr/expressions/expression_utils.py", line 150, in _eval_many
    return Env.backend().execute_many(*irs, timed=timed)
  File "/home/edmund/.local/src/hail/hail/python/hail/backend/backend.py", line 38, in execute_many
    return [self.execute(MakeTuple([ir]), timed=timed)[0] for ir in irs]
  File "/home/edmund/.local/src/hail/hail/python/hail/backend/backend.py", line 38, in <listcomp>
    return [self.execute(MakeTuple([ir]), timed=timed)[0] for ir in irs]
  File "/home/edmund/.local/src/hail/hail/python/hail/backend/py4j_backend.py", line 94, in execute
    jir = self._to_java_value_ir(ir)
  File "/home/edmund/.local/src/hail/hail/python/hail/backend/spark_backend.py", line 280, in _to_java_value_ir
    return self._to_java_ir(ir, self._parse_value_ir)
  File "/home/edmund/.local/src/hail/hail/python/hail/backend/spark_backend.py", line 276, in _to_java_ir
    ir._jir = parse(r(finalize_randomness(ir)), ir_map=r.jirs)
  File "/home/edmund/.local/src/hail/hail/python/hail/backend/spark_backend.py", line 245, in _parse_value_ir
    return self._jbackend.parse_value_ir(
  File "/home/edmund/.local/src/hail/.venv/lib/python3.8/site-packages/py4j/java_gateway.py", line 1304, in __call__
    return_value = get_return_value(
  File "/home/edmund/.local/src/hail/hail/python/hail/backend/py4j_backend.py", line 21, in deco
    return f(*args, **kwargs)
  File "/home/edmund/.local/src/hail/.venv/lib/python3.8/site-packages/py4j/protocol.py", line 326, in get_return_value
    raise Py4JJavaError(
py4j.protocol.Py4JJavaError: An error occurred while calling o1.parse_value_ir.
: java.util.NoSuchElementException: key not found: __uid_4
        at scala.collection.immutable.Map$Map1.apply(Map.scala:114)
        at is.hail.expr.ir.Env.apply(Env.scala:128)
        at is.hail.expr.ir.IRParser$.ir_value_expr_1(Parser.scala:890)
        at is.hail.expr.ir.IRParser$.$anonfun$ir_value_expr$1(Parser.scala:820)
        at is.hail.utils.StackSafe$More.advance(StackSafe.scala:64)
        at is.hail.utils.StackSafe$.run(StackSafe.scala:16)
        at is.hail.utils.StackSafe$StackFrame.run(StackSafe.scala:32)
        at is.hail.expr.ir.IRParser$.$anonfun$parse_value_ir$1(Parser.scala:2072)
        at is.hail.expr.ir.IRParser$.parse(Parser.scala:2068)
        at is.hail.expr.ir.IRParser$.parse_value_ir(Parser.scala:2072)
        at is.hail.backend.spark.SparkBackend.$anonfun$parse_value_ir$2(SparkBackend.scala:710)
        at is.hail.backend.ExecuteContext$.$anonfun$scoped$3(ExecuteContext.scala:70)
        at is.hail.utils.package$.using(package.scala:635)
        at is.hail.backend.ExecuteContext$.$anonfun$scoped$2(ExecuteContext.scala:70)
        at is.hail.utils.package$.using(package.scala:635)
        at is.hail.annotations.RegionPool$.scoped(RegionPool.scala:17)
        at is.hail.backend.ExecuteContext$.scoped(ExecuteContext.scala:59)
        at is.hail.backend.spark.SparkBackend.withExecuteContext(SparkBackend.scala:339)
        at is.hail.backend.spark.SparkBackend.$anonfun$parse_value_ir$1(SparkBackend.scala:709)
        at is.hail.utils.ExecutionTimer$.time(ExecutionTimer.scala:52)
        at is.hail.utils.ExecutionTimer$.logTime(ExecutionTimer.scala:59)
        at is.hail.backend.spark.SparkBackend.parse_value_ir(SparkBackend.scala:708)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:566)
        at py4j.reflection.MethodInvoker.invoke(MethodInvoker.java:244)
        at py4j.reflection.ReflectionEngine.invoke(ReflectionEngine.java:357)
        at py4j.Gateway.invoke(Gateway.java:282)
        at py4j.commands.AbstractCommand.invokeMethod(AbstractCommand.java:132)
        at py4j.commands.CallCommand.execute(CallCommand.java:79)
        at py4j.GatewayConnection.run(GatewayConnection.java:238)
        at java.base/java.lang.Thread.run(Thread.java:834)

Note that the following works (removing the literal):

# use homogeneous array type
variants = [("10", 123, ["G", "C"]), ("10", 456, ["T", "A"])]

# `x` is already an expression, with the literal array captured by `any`
expr = hl.any(
  lambda x:
    (mt.locus.contig == x[0]) & \
    (mt.locus.position == x[1]) & \
    (mt.alleles == x[2]),
  variants
)

hl.eval(expr)

@ehigham
Copy link
Member

ehigham commented Jul 6, 2023

I believe the code is behaving as designed. The error message, however, leaves a lot to be desired.

A few take-aways:

  1. Hail doesn't support heterogeneous arrays. In situations like these, using an array of tuples has the desired outcome.
  2. The variable x in lambda x: is already a hail expression and so you don't need to explictly capture it as a literal.
    a. While support for using hail expressions with literal was added in Permit expressions inside literal #4086 (see the issue for motivation), it can only be used when that expression is self-contained (ie it's not dependent on another hail expression, eg referencing an element of a hail array expression or tuple expression etc).
    b. Our evaluation strategy is to eval the expression, then broadcast the result in a literal.
    c. eval correctly complains that that expression has free variables and so can't be evaluated.
    d. This error is ugly and has little to do with what the user wanted to achieve.

Off the top of my head, a couple of ways to proceed:

  1. The hardest (but backwards compatible) fix is to somehow provide a good error message that the x in lambda x: in this particular context is a hail expression containing a reference that you should not use with literal.
  2. Remove support for using hail expressions with literal.

@ehigham
Copy link
Member

ehigham commented Jul 7, 2023

Ah - I miss read the orignal forum post, something about scrollbars within scrollbars always confuses me. Onto the ClassCastException now.

@ehigham
Copy link
Member

ehigham commented Jul 7, 2023

Not reproducable in main (3e0bb6f)

ehigham added a commit to ehigham/hail that referenced this issue Jul 7, 2023
@ehigham ehigham closed this as completed Jul 7, 2023
danking pushed a commit that referenced this issue Jul 7, 2023
RR: #13045
RR: #13046 
Support symmetric comparison of structs and struct expressions.
Provide better error messages when attempting to construct literals from
expressions with free variables.
@danking
Copy link
Contributor Author

danking commented Jul 10, 2023

Hmm. The ClassCastException in the linked comment does indeed seem not reproducible. I've asked for more detail from Simon https://discuss.hail.is/t/subset-matrix-table-to-a-medium-sized-list-of-variants/3362/9?u=danking

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants